home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Development Tools & Languages / Dylan Related / Mindy / Mindy 1.2 - portable sources / libraries / streams / streams.dylan < prev    next >
Encoding:
Text File  |  1995-03-15  |  92.3 KB  |  2,721 lines  |  [TEXT/ttxt]

  1. module: Streams
  2. author: chiles@cs.cmu.edu
  3. synopsis: This file implements streams for the Gwydion implementation of Dylan.
  4. copyright: See below.
  5. rcs-header: $Header: streams.dylan,v 1.25 94/11/16 13:57:49 chiles Exp $
  6.  
  7. //======================================================================
  8. //
  9. // Copyright (c) 1994  Carnegie Mellon University
  10. // All rights reserved.
  11. // 
  12. // Use and copying of this software and preparation of derivative
  13. // works based on this software are permitted, including commercial
  14. // use, provided that the following conditions are observed:
  15. // 
  16. // 1. This copyright notice must be retained in full on any copies
  17. //    and on appropriate parts of any derivative works.
  18. // 2. Documentation (paper or online) accompanying any system that
  19. //    incorporates this software, or any part of it, must acknowledge
  20. //    the contribution of the Gwydion Project at Carnegie Mellon
  21. //    University.
  22. // 
  23. // This software is made available "as is".  Neither the authors nor
  24. // Carnegie Mellon University make any warranty about the software,
  25. // its performance, or its conformity to any specification.
  26. // 
  27. // Bug reports, questions, comments, and suggestions should be sent by
  28. // E-mail to the Internet address "gwydion-bugs@cs.cmu.edu".
  29. //
  30. //======================================================================
  31.  
  32.  
  33.  
  34. //// Constants.
  35. ////
  36.  
  37. define constant $maximum-buffer-size = $maximum-fixed-integer;
  38.  
  39. define constant $default-buffer-size = 2000;
  40.  
  41. define constant <buffer-index> =
  42.   limited(<fixed-integer>, min: 0, max: $maximum-buffer-size);
  43.  
  44.  
  45.  
  46. //// Some classes (including conditions).
  47. ////
  48.  
  49. /// <stream> Class -- Exported.
  50. ///
  51. /// All other streams inherit from this class.
  52. ///
  53. /// Though all streams have buffers, or appear to have buffers, subclasses
  54. /// of the <stream> class cannot inherit the buffer slot from this class
  55. /// because the stream interface makes no provision for implementors of new
  56. /// streams to fetch the buffer.
  57. ///
  58. define abstract class <stream> (<object>)
  59.   //
  60.   // See the generic function stream-locked? for how this slot is used.
  61.   slot stream-lock :: <multilock>,
  62.     init-function: method () make(<multilock>) end;
  63.   //
  64.   // Yes, users of this module that implement their own streams will get
  65.   // this slot and be unable to use it because there is no interface to it.
  66.   // Oh well.
  67.   slot buffer-locked? :: <boolean>, init-value: #f;
  68. end class;
  69.  
  70. /// <random-access-stream> Class -- Exported.
  71. ///
  72. /// All required streams inherit from this class.
  73. ///
  74. define abstract class <random-access-stream> (<stream>)
  75. end class;
  76.  
  77.  
  78. ///
  79. /// Conditions.
  80. ///
  81.  
  82. /// These are all exported.
  83. ///
  84.  
  85. define class <end-of-file> (<error>)
  86.   slot end-of-file-stream :: <stream>, init-keyword: #"stream";
  87. end class;
  88.  
  89. define class <file-not-found> (<error>)
  90.   slot file-not-found-filename :: <string>, init-keyword: #"filename";
  91. end class;
  92.  
  93. define class <file-exists> (<error>)
  94.   slot file-exists-filename :: <string>, init-keyword: #"filename";
  95. end class;
  96.  
  97.  
  98.  
  99. //// Stream locking.
  100. ////
  101.  
  102. /// stream-locked? -- Exported.
  103. ///
  104. /// This function returns whether the stream is currently held (in use) by
  105. /// the application.  Only one thread of the application may use the stream
  106. /// at one time.  Having the stream locked is different than holding the
  107. /// buffer.  The stream must be locked before a thread can get the buffer,
  108. /// and the stream may be locked across multiple calls to functions that get
  109. /// and release the buffer.  Streams use a <multilock> from the Threads
  110. /// module of the Dylan library so that a single thread may repeatedly lock
  111. /// the stream.
  112. ///
  113. /// The distinction between locking the stream and the buffer provides a
  114. /// couple of advantages.  The stream's lock prohibits more than one thread
  115. /// from having the ability to hold the buffer.  Having a separate soft lock
  116. /// for the buffer means that a single thread of execution is safe from
  117. /// inadvertently calling a function that diddles the buffer while the
  118. /// thread is already diddling the buffer.
  119. ///
  120. define generic stream-locked? (stream :: <stream>) => locked? :: <boolean>;
  121.  
  122. define method stream-locked? (stream :: <stream>) => locked? :: <boolean>;
  123.   locked?(stream.stream-lock);
  124. end method;
  125.  
  126. /// lock-stream -- Exported.
  127. ///
  128. define generic lock-stream (stream :: <stream>) => ();
  129.  
  130. define method lock-stream (stream :: <stream>) => ();
  131.   grab-lock(stream.stream-lock);
  132. end method;
  133.  
  134. /// unlock-stream -- Exported.
  135. ///
  136. define generic unlock-stream (stream :: <stream>) => ();
  137.  
  138. define method unlock-stream (stream :: <stream>) => ();
  139.   release-lock(stream.stream-lock);
  140. end method;
  141.  
  142.  
  143.  
  144. //// Internal protocol for streams.
  145. ////
  146.  
  147. ///
  148. /// Buffer locking.
  149. /// 
  150.  
  151. /// buffer-locked? -- Internal Interface.
  152. ///
  153. /// This function returns whether the buffer is currently in use.  Only one
  154. /// thread of the application may use the buffer at one time, which is
  155. /// enforced by locking the stream.  Functions that lock the stream and then
  156. /// get the buffer cannot call other functions that get the buffer, unless
  157. /// the first function releases the buffer before calling the second
  158. /// function; buffers are NOT locked with multilocking semantics.  See the
  159. /// comment for the generic function stream-locked? for more information.
  160. ///
  161. /// This function is implemented as a slot in the <stream> class.
  162. /// Applications must lock the stream before calling this function.
  163. ///
  164. define generic buffer-locked? (stream :: <stream>) => locked? :: <boolean>;
  165.  
  166. /// buffer-locked?-setter -- Internal Interface.
  167. ///
  168. /// This function is implemented as a slot in the <stream> class.
  169. /// Applications must lock the stream before calling this function.
  170. ///
  171. define generic buffer-locked?-setter (value :: <boolean>, stream :: <stream>)
  172.     => locked? :: <boolean>;
  173.  
  174.  
  175. ///
  176. /// Buffer access, next values, and stop values.
  177. ///
  178.  
  179. /// buffer -- Internal Interface.
  180. ///
  181. /// This function returns the buffer or #f.  Streams should set the buffer to
  182. /// #f when the stream is closed.  This function can be a test for whether the
  183. /// stream is still open.
  184. ///
  185. /// This function is typically implemented as a slot in the stream's class,
  186. /// but some streams may want to implement it virtually (on demand) when
  187. /// users insist on using the stream's buffer directly.
  188. ///
  189. define generic buffer (stream :: <stream>)
  190.     => buffer :: false-or(<buffer>);
  191.  
  192. /// buffer-setter -- Internal Interface.
  193. ///
  194. define generic buffer-setter (value :: false-or(<buffer>), stream :: <stream>)
  195.     => value :: false-or(<buffer>);
  196.  
  197. /// buffer-next -- Internal Interface.
  198. ///
  199. /// This function is implemented as slots in class definitions.  See the
  200. /// class definitiond for what the return value means.
  201. ///
  202. define generic buffer-next (stream :: <stream>) => next :: <buffer-index>;
  203.  
  204. /// buffer-stop -- Internal Interface.
  205. ///
  206. /// This function is implemented as slots in class definitions.  See the
  207. /// class definitiond for what the return value means.
  208. ///
  209. define generic buffer-stop (stream :: <stream>) => stop :: <buffer-index>;
  210.  
  211.  
  212. ///
  213. /// Output stream registration and forcing output upon Application exit.
  214. ///
  215.  
  216. /// This lock isolates access to *output-streams*.
  217. ///
  218. define constant output-stream-registry-lock = make(<semaphore>);
  219.  
  220. /// This list contains all open output streams.  There is a function
  221. /// registered on the exist hook that forces output on all streams when the
  222. /// application exits.
  223. ///
  224. define variable *output-streams* = #();
  225.  
  226. /// register-output-stream -- Internal Interface.
  227. ///
  228. /// This function registers output functions for the purpose of
  229. /// synchronizing their output when an application exits.  The same registry
  230. /// of streams could be used by a demon thread that periodically wakes up
  231. /// and forces output on streams.
  232. ///
  233. define method register-output-stream (stream :: <stream>) => stream :: <stream>;
  234.   grab-lock(output-stream-registry-lock);
  235.   *output-streams* := pair(stream, *output-streams*);
  236.   release-lock(output-stream-registry-lock);
  237.   stream;
  238. end method;
  239.  
  240. /// unregister-output-stream -- Internal Interface.
  241. ///
  242. /// This function removes stream from *output-streams*.
  243. ///
  244. define method unregister-output-stream (stream :: <stream>)
  245.     => stream :: <stream>;
  246.   grab-lock(output-stream-registry-lock);
  247.   *output-streams* := remove!(*output-streams*, stream);
  248.   release-lock(output-stream-registry-lock);
  249.   stream;
  250. end method;
  251.  
  252. /// Register a function on the application exit hook.  This function forces
  253. /// output for every output stream.  There's no reason to isolate access to
  254. /// *output-streams* because exit functions run one at a time in the only
  255. /// remaining thread.
  256. ///
  257. on-exit(method ()
  258.       for (stream in *output-streams*)
  259.         synchronize-output(stream);
  260.       end;
  261.       end);
  262.  
  263.  
  264.  
  265. //// Stream Extension Protocol.
  266. ////
  267.  
  268. /// All of these are exported.
  269. ///
  270.  
  271. define generic close (stream :: <stream>) => ();
  272.  
  273. define generic stream-extension-get-input-buffer (stream :: <stream>)
  274.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  275.  
  276. define generic stream-extension-release-input-buffer
  277.     (stream :: <stream>, next :: <buffer-index>, stop :: <buffer-index>)
  278.     => ();
  279.  
  280. define generic stream-extension-fill-input-buffer
  281.     (stream :: <stream>, start :: <buffer-index>)
  282.     => stop :: <buffer-index>;
  283.  
  284. define generic stream-extension-input-available-at-source? (stream :: <stream>)
  285.     => available? :: <boolean>;
  286.  
  287. define generic stream-extension-get-output-buffer (stream :: <stream>)
  288.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  289.  
  290. define generic stream-extension-release-output-buffer
  291.     (stream :: <stream>, next :: <buffer-index>)
  292.     => ();
  293.  
  294. define generic stream-extension-empty-output-buffer
  295.     (stream :: <stream>, stop :: <buffer-index>)
  296.     => ();
  297.  
  298.  
  299. define generic stream-extension-force-secondary-buffers (stream :: <stream>)
  300.     => ();
  301.  
  302. /// stream-extension-force-secondary-buffers -- Method for Exported Interface.
  303. ///
  304. /// This Stream Extension Protocol function gets a default method because
  305. /// most streams will not need to extend this function.  Most streams do
  306. /// not do secondary buffering, so most stream implementors can ignore this.
  307. /// Unfortunately, uses cannot and must call it when forcing outout.
  308. ///
  309. define method stream-extension-force-secondary-buffers (stream :: <stream>)
  310.     => ();
  311. end method;
  312.  
  313.  
  314. define generic stream-extension-synchronize (stream :: <stream>) => ();
  315.  
  316.  
  317.  
  318. //// Basic I/O Protocol.
  319. ////
  320.  
  321. /// All of these are exported.
  322. ///
  323.  
  324. define generic read-byte (stream :: <stream>,
  325.               #key signal-eof?: :: <boolean>) // = #t
  326.     => byte :: false-or(<byte>);
  327.  
  328. define method read-byte (stream :: <stream>,
  329.              #key signal-eof? :: <boolean> = #t)
  330.     => byte :: false-or(<byte>);
  331.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  332.     = get-input-buffer(stream);
  333.   if (next == stop)
  334.     stop := fill-input-buffer(stream, 0);
  335.     next := 0;
  336.   end;
  337.   if (stop ~= 0)
  338.     let res = buf[next];
  339.     release-input-buffer(stream, next + 1, stop);
  340.     res;
  341.   elseif (signal-eof?)
  342.     release-input-buffer(stream, 0, 0);
  343.     error(make(<end-of-file>, stream: stream));
  344.   else
  345.     release-input-buffer(stream, 0, 0);
  346.     #f;
  347.   end;
  348. end method;
  349.  
  350.  
  351. define generic peek-byte (stream :: <stream>) => byte :: false-or(<byte>);
  352.  
  353. define method peek-byte (stream :: <stream>) => byte :: false-or(<byte>);
  354.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  355.     = get-input-buffer(stream);
  356.   if (next == stop)
  357.     stop := fill-input-buffer(stream, 0);
  358.     next := 0;
  359.   end;
  360.   if (stop ~= 0)
  361.     let res = buf[next];
  362.     release-input-buffer(stream, next, stop);
  363.     res;
  364.   else
  365.     release-input-buffer(stream, 0, 0);
  366.     #f;
  367.   end;
  368. end method;
  369.  
  370.  
  371. define generic read-line (stream :: <stream>,
  372.               #key signal-eof?: :: <boolean>) // = #t
  373.     => (result :: false-or(<string>), eof? :: <boolean>);
  374.  
  375. /// This could be a literal constant in the following method definition, but
  376. /// Dylan failed to incorporate any means for cleanly identifying non-printing
  377. /// characters in character and string literals.  I don't want to use my
  378. /// editor to quote non-printing characters into my program's source.
  379. ///
  380. define constant $newline-byte = as(<byte>, '\n');
  381.  
  382. /// This cannot use a big global buffer to build the result because of
  383. /// thread-safety.  The intermediate result consing should be rare or minimal,
  384. /// assuming a reasonable relationship between line lengths and the buffer's
  385. /// length.
  386. ///
  387. define method read-line (stream :: <stream>,
  388.              #key signal-eof? :: <boolean> = #t)
  389.     => (result :: false-or(<string>), eof? :: <boolean>);
  390.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  391.     = get-input-buffer(stream);
  392.   // Make sure we have input, if there is any.
  393.   if (next == stop)
  394.     stop := fill-input-buffer(stream, 0);
  395.     next := 0;
  396.   end;
  397.   case
  398.     (stop ~= 0) =>
  399.       // We definitely have some input available.
  400.       block (exit-loop)
  401.     let res = "";
  402.     let collect = method (string :: <byte-string>, buf :: <buffer>,
  403.                   start :: <buffer-index>, stop :: <buffer-index>)
  404.             let str-len = string.size;
  405.             let buf-len = (stop - start);
  406.             let res = make(<byte-string>,
  407.                        size: (str-len + buf-len));
  408.             copy-bytes(res, 0, string, 0, str-len);
  409.             copy-bytes(res, str-len, buf, start, buf-len);
  410.             res;
  411.               end;
  412.     while (#t)
  413.       for (i from next below stop,
  414.            until (buf[i] = $newline-byte))
  415.       finally
  416.         if (i = stop)
  417.           res := collect(res, buf, next, stop);
  418.         else
  419.           res := collect(res, buf, next, i);
  420.           // We don't return the newline, but we do consume it.
  421.           release-input-buffer(stream, (i + 1), stop);
  422.           exit-loop(res, #f);
  423.         end;
  424.       end;
  425.       next := 0;
  426.       stop := fill-input-buffer(stream, 0);
  427.       if (stop = 0)
  428.         release-input-buffer(stream, 0, 0);
  429.         exit-loop(res, #t);
  430.       end;
  431.     end while;
  432.       end block;
  433.     (signal-eof?) =>
  434.       // Hit EOF immediately.
  435.       release-input-buffer(stream, 0, 0);
  436.       error(make(<end-of-file>, stream: stream));
  437.     otherwise =>
  438.       // Hit EOF immediately.
  439.       release-input-buffer(stream, 0, 0);
  440.       values(#f, #t);
  441.   end case;
  442. end method;
  443.  
  444.  
  445. define generic input-available? (stream :: <stream>) => result :: <boolean>;
  446.  
  447. define method input-available? (stream :: <stream>) => result :: <boolean>;
  448.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  449.     = get-input-buffer(stream);
  450.   if (next == stop)
  451.     let res = input-available-at-source?(stream);
  452.     release-input-buffer(stream, 0, 0);
  453.     res;
  454.   else
  455.     release-input-buffer(stream, next, stop);
  456.     #t
  457.   end;
  458. end method;
  459.  
  460.  
  461. define generic flush-input (stream :: <stream>) => ();
  462.  
  463. define method flush-input (stream :: <stream>) => ();
  464.   let buf :: <buffer> = get-input-buffer(stream);
  465.   for (until (fill-input-buffer(stream, 0) = 0))
  466.   end;
  467.   release-input-buffer(stream, 0, 0);
  468. end method;
  469.  
  470.  
  471. define generic force-output (stream :: <stream>) => ();
  472.  
  473. define method force-output (stream :: <stream>) => ();
  474.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  475.   if (next ~= 0)
  476.     empty-output-buffer(stream, next);
  477.   end;
  478.   force-secondary-buffers(stream);
  479.   release-output-buffer(stream, 0);
  480. end method;
  481.  
  482.  
  483. define generic synchronize-output (stream :: <stream>) => ();
  484.  
  485. define method synchronize-output (stream :: <stream>) => ();
  486.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  487.   if (next ~= 0)
  488.     empty-output-buffer(stream, next);
  489.   end;
  490.   force-secondary-buffers(stream);
  491.   synchronize(stream);
  492.   release-output-buffer(stream, 0);
  493. end method;
  494.  
  495.  
  496.  
  497. //// Buffer Access Protocol.
  498. ////
  499.  
  500. /// All of these are exported.
  501. ///
  502. /// This page contains the generic function declarations as well as a default
  503. /// implementation for <stream>s.
  504. ///
  505.  
  506.  
  507. /// get-input-buffer -- Exported.
  508. ///
  509. define sealed generic get-input-buffer (stream :: <stream>)
  510.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  511.  
  512. define method get-input-buffer (stream :: <stream>)
  513.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  514.   // Isolate the calling thread's access to the stream.
  515.   lock-stream(stream);
  516.   // Make sure the thread does not already hold the buffer.
  517.   if (stream.buffer-locked?)
  518.     error("Application already holds stream's buffer -- %=.", stream);
  519.   else
  520.     stream.buffer-locked? := #t;
  521.   end;
  522.   stream-extension-get-input-buffer(stream);
  523. end method;
  524.  
  525. /// release-input-buffer -- Exported.
  526. ///
  527. define sealed generic release-input-buffer
  528.     (stream :: <stream>, next :: <buffer-index>, stop :: <buffer-index>)
  529.     => ();
  530.  
  531. define method release-input-buffer
  532.     (stream :: <stream>, next :: <buffer-index>, stop :: <buffer-index>)
  533.     => ();
  534.   check-buffer-held(stream);
  535.   stream-extension-release-input-buffer(stream, next, stop);
  536.   stream.buffer-locked? := #f;
  537.   // Unlock the lock obtained in get-input-buffer.
  538.   unlock-stream(stream);
  539. end method;
  540.  
  541. /// fill-input-buffer -- Exported.
  542. ///
  543. define sealed generic fill-input-buffer (stream :: <stream>,
  544.                      start :: <buffer-index>)
  545.     => stop :: <buffer-index>;
  546.  
  547. define method fill-input-buffer (stream :: <stream>, start :: <buffer-index>)
  548.     => stop :: <buffer-index>;
  549.   check-buffer-held(stream);
  550.   stream-extension-fill-input-buffer(stream, start);
  551. end method;
  552.  
  553. /// input-available-at-source? -- Exported.
  554. ///
  555. define sealed generic input-available-at-source? (stream :: <stream>)
  556.     => available? :: <boolean>;
  557.  
  558. define method input-available-at-source? (stream :: <stream>)
  559.     => available? :: <boolean>;
  560.   check-buffer-held(stream);
  561.   stream-extension-input-available-at-source?(stream);
  562. end method;
  563.  
  564. /// get-output-buffer -- Exported.
  565. ///
  566. define sealed generic get-output-buffer (stream :: <stream>)
  567.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  568.  
  569. define method get-output-buffer (stream :: <stream>)
  570.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  571.   // Isolate the calling thread's access to the stream.
  572.   lock-stream(stream);
  573.   // Make sure the thread does not already hold the buffer.
  574.   if (stream.buffer-locked?)
  575.     error("Application already holds stream's buffer -- %=.", stream);
  576.   else
  577.     stream.buffer-locked? := #t;
  578.   end;
  579.   stream-extension-get-output-buffer(stream);
  580. end method;
  581.  
  582. /// release-output-buffer -- Exported.
  583. ///
  584. define sealed generic release-output-buffer
  585.     (stream :: <stream>, next :: <buffer-index>)
  586.     => ();
  587.  
  588. define method release-output-buffer (stream :: <stream>, next :: <buffer-index>)
  589.     => ();
  590.   check-buffer-held(stream);
  591.   stream-extension-release-output-buffer(stream, next);
  592.   stream.buffer-locked? := #f;
  593.   // Unlock the lock obtained in get-input-buffer.
  594.   unlock-stream(stream);
  595. end method;
  596.  
  597. /// empty-output-buffer -- Exported.
  598. ///
  599. define sealed generic empty-output-buffer (stream :: <stream>,
  600.                        stop :: <buffer-index>)
  601.     => ();
  602.  
  603. define method empty-output-buffer (stream :: <stream>, stop :: <buffer-index>)
  604.     => ();
  605.   check-buffer-held(stream);
  606.   stream-extension-empty-output-buffer(stream, stop);
  607. end method;
  608.  
  609. /// force-secondary-buffers -- Exported.
  610. ///
  611. define sealed generic force-secondary-buffers (stream :: <stream>) => ();
  612.  
  613. define method force-secondary-buffers (stream :: <stream>) => ();
  614.   check-buffer-held(stream);
  615.   stream-extension-force-secondary-buffers(stream);
  616. end method;
  617.  
  618. /// synchronize -- Exported.
  619. ///
  620. define sealed generic synchronize (stream :: <stream>) => ();
  621.  
  622. define method synchronize (stream :: <stream>) => ();
  623.   check-buffer-held(stream);
  624.   stream-extension-synchronize(stream);
  625. end method;
  626.  
  627. /// check-buffer-held -- Internal Interface.
  628. ///
  629. /// After calling this function, the executing thread is guaranteed to have
  630. /// the stream locked and to hold the buffer.
  631. ///
  632. define method check-buffer-held (stream :: <stream>) => ();
  633.   // Lock the stream to isolate checking whether the buffer is locked.
  634.   lock-stream(stream);
  635.   if (~ stream.buffer-locked?)
  636.     unlock-stream(stream);
  637.     error("Application does not hold stream's buffer -- %=.", stream);
  638.   end;
  639.   // Unlock the lock for checking buffer-locked?.
  640.   unlock-stream(stream);
  641.   // Because the buffer was locked, and we were able to obtain a lock, the
  642.   // calling thread must already hold a lock on the stream due to
  643.   // get-output-buffer.  Therefore, code following the call to
  644.   // check-buffer-held is still thread-safe, until the final lock is
  645.   // dropped.
  646. end method;
  647.  
  648.  
  649.  
  650. //// Data Extension Protocol.
  651. ////
  652.  
  653. /// read-as -- Exported.
  654. ///
  655. define generic read-as (result-class :: <class>, stream :: <stream>,
  656.             #key signal-eof?: :: <boolean>) // = #t
  657.     => (result :: false-or(<object>), eof? :: <boolean>);
  658.  
  659.  
  660. define sealed method read-as
  661.     (result-class :: singleton(<byte-character>), stream :: <stream>,
  662.      #key signal-eof? :: <boolean> = #t)
  663.     => (result :: false-or(<byte-character>), eof? :: <boolean>);
  664.   let res :: false-or(<byte>)
  665.     = read-byte(stream, signal-eof?: signal-eof?);
  666.   // If read-byte returns, we either have a byte or signal-eof? was #f.
  667.   if (res)
  668.     values(as(<byte-character>, res), #f)
  669.   else
  670.     values(#f, #t);
  671.   end;
  672. end method;
  673.  
  674. define sealed method read-as
  675.     (result-class :: singleton(<byte>), stream :: <stream>,
  676.      #key signal-eof? :: <boolean> = #t)
  677.     => (result :: false-or(<byte>), eof? :: <boolean>);
  678.   let res :: false-or(<byte>) = read-byte(stream, signal-eof?: signal-eof?);
  679.   values(res, if (res) #f else #t end);
  680. end method;
  681.  
  682.  
  683.  
  684. /// read-as for <byte-string> and <byte-vector> results from <stream>s.
  685. ///
  686. /// Read-as for <byte-string> and <byte-vector> have the same definition.
  687. /// There are two "define method" forms so that the distinct return types
  688. /// can be distinctly declared.  If the "seal generic" form allowed you to
  689. /// declare return types, there could be one method here with two "seal
  690. /// generic" forms declaring the distinct specializations and their
  691. /// associated return types.
  692. ///
  693.  
  694. define method read-as
  695.     (result-class :: singleton(<byte-string>), stream :: <stream>,
  696.      #key signal-eof? :: <boolean> = #t,
  697.           count :: false-or(<fixed-integer>),
  698.           to-eof? :: <boolean> = #f)
  699.     => (result :: false-or(<byte-string>), eof? :: <boolean>);
  700.   case
  701.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  702.                          count);
  703.     (to-eof?) => read-as-required-vector-to-eof(stream, result-class);
  704.     otherwise =>
  705.       error("Count or to-eof? must be supplied to read a <byte-string>.");
  706.   end;
  707. end method;
  708.  
  709. define method read-as
  710.     (result-class :: singleton(<byte-vector>), stream :: <stream>,
  711.      #key signal-eof? :: <boolean> = #t,
  712.           count :: false-or(<fixed-integer>),
  713.           to-eof? :: <boolean> = #f)
  714.     => (result :: false-or(<byte-vector>), eof? :: <boolean>);
  715.   case
  716.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  717.                          count);
  718.     (to-eof?) => read-as-required-vector-to-eof(stream, result-class);
  719.     otherwise =>
  720.       error("Count or to-eof? must be supplied to read a <byte-vector>.");
  721.   end;
  722. end method;
  723.  
  724. /// read-as-required-vector-count -- Internal.
  725. ///
  726. /// This function implements read-as for <byte-string> and <byte-vector> for
  727. /// any stream when the user supplied a count: argument.  This function
  728. /// works for <buffer>s too due to the use of copy-bytes, but reading
  729. /// buffers is implemented for each stream type to avoid double buffering.
  730. ///
  731. define method read-as-required-vector-count
  732.     (stream :: <stream>,
  733.      result-class :: one-of(<byte-vector>, <byte-string>),
  734.      signal-eof? :: <boolean>,
  735.      count :: <fixed-integer>)
  736.     => (result :: type-or(<byte-vector>, <byte-string>, singleton(#f)),
  737.     eof? :: <boolean>);
  738.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  739.     = get-input-buffer(stream);
  740.   let result = make(result-class, size: count);
  741.   if (next == stop)
  742.     stop := fill-input-buffer(stream, 0);
  743.     next := 0;
  744.   end;
  745.   block (exit-loop)
  746.     let available :: <buffer-index> = (stop - next);
  747.     let result-start :: <fixed-integer> = 0;
  748.     let buf-start :: <buffer-index> = next;
  749.     for (until (available = 0))
  750.       let result-stop :: <fixed-integer> = (result-start + available);
  751.       if (result-stop >= count)
  752.     let this-copy = (count - result-start);
  753.     copy-bytes(result, result-start, buf, buf-start, this-copy);
  754.     release-input-buffer(stream, (buf-start + this-copy),
  755.                  // Can't assume buf-start is 0 because we may
  756.                  // come in here on the first iteration.
  757.                  (buf-start + available));
  758.     exit-loop(result, #f);
  759.       else
  760.     copy-bytes(result, result-start, buf, buf-start, available);
  761.       end;
  762.       available := fill-input-buffer(stream, 0);
  763.       result-start := result-stop;
  764.       buf-start := 0;
  765.     finally
  766.       // Whenever the loop terminates normally, we don't have enough input
  767.       // to satisfy the request.
  768.       release-input-buffer(stream, 0, 0);
  769.       if (signal-eof?)
  770.     error(make(<end-of-file>, stream: stream));
  771.       else
  772.     values(#f, #t);
  773.       end;
  774.     end for;
  775.   end block;
  776. end method;
  777.  
  778. /// read-as-required-vector-to-eof -- Internal.
  779. ///
  780. /// This function implements read-as for <byte-string>, <byte-vector>, and
  781. /// <buffer> for any stream when the user supplied a to-eof?: argument.
  782. /// There are better methods for <random-access-stream> and
  783. /// <fd-file-stream>.  If the users can't know the size of the stream, are
  784. /// using read-as to read to-eof, and asking for a <buffer> result, then
  785. /// they may as well get poor performance :-); seriously, they should be
  786. /// using the buffer directly.  The scenario described is a pretty unlikely
  787. /// one too.
  788. ///
  789. /// This function cannot assume the Random Access Protocol, so it must
  790. /// repeatedly fill the buffer and build intermediate results to satisfy the
  791. /// read request.  This function cannot use a big global buffer to build the
  792. /// result because of thread-safety.
  793. ///
  794. define method read-as-required-vector-to-eof
  795.     (stream :: <stream>,
  796.      result-class :: one-of(<byte-vector>, <byte-string>, <buffer>))
  797.     => (result :: type-or(<byte-vector>, <byte-string>, <buffer>),
  798.     eof? :: singleton(#t));
  799.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  800.     = get-input-buffer(stream);
  801.   // Make sure we have input if there is any.
  802.   if (next == stop)
  803.     stop := fill-input-buffer(stream, 0);
  804.     next := 0;
  805.   end;
  806.   let res = make(result-class, size: 0);
  807.   let res-len = 0;
  808.   for (next = next then 0,
  809.        stop = stop then fill-input-buffer(stream, 0),
  810.        until (stop = 0))
  811.     let buf-len = (stop - next);
  812.     let temp-len = (res-len + buf-len);
  813.     let temp = make(<byte-string>, size: temp-len);
  814.     copy-bytes(temp, 0, res, 0, res-len);
  815.     copy-bytes(temp, res-len, buf, next, buf-len);
  816.     res := temp;
  817.     res-len := temp-len;
  818.   finally
  819.     release-input-buffer(stream, 0, 0);
  820.     values(res, #t);
  821.   end for;
  822. end method;
  823.  
  824.  
  825.  
  826. /// read-as for <byte-string> and <byte-vector> results from <random-access-stream>s
  827. ///
  828. /// Read-as for <byte-string> and <byte-vector> have the same definition.
  829. /// There are two "define method" forms so that the distinct return types
  830. /// can be distinctly declared.  If the "seal generic" form allowed you to
  831. /// declare return types, there could be one method here with two "seal
  832. /// generic" forms declaring the distinct specializations and their
  833. /// associated return types.
  834. ///
  835.  
  836. define method read-as
  837.     (result-class :: singleton(<byte-string>), stream :: <random-access-stream>,
  838.      #key signal-eof? :: <boolean> = #t,
  839.           count :: false-or(<fixed-integer>),
  840.           to-eof? :: <boolean> = #f)
  841.     => (result :: false-or(<byte-string>), eof? :: <boolean>);
  842.   case
  843.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  844.                          count);
  845.     (to-eof?) =>
  846.       // Isolate thread access across this call so that no thread intervenes
  847.       // between the calls to stream-size, stream-position, and read-as-r....
  848.       lock-stream(stream);
  849.       let res = read-as-required-vector-count(stream, result-class, #f,
  850.                           (stream.stream-size
  851.                          - stream.stream-position));
  852.       unlock-stream(stream);
  853.       values(res, #t);
  854.     otherwise =>
  855.       error("Count or to-eof? must be supplied to read a <byte-string>.");
  856.   end;
  857. end method;
  858.  
  859. define method read-as
  860.     (result-class :: singleton(<byte-vector>), stream :: <random-access-stream>,
  861.      #key signal-eof? :: <boolean> = #t,
  862.           count :: false-or(<fixed-integer>),
  863.           to-eof? :: <boolean> = #f)
  864.     => (result :: false-or(<byte-vector>), eof? :: <boolean>);
  865.   case
  866.     (count) => read-as-required-vector-count(stream, result-class, signal-eof?,
  867.                          count);
  868.     (to-eof?) =>
  869.       // Isolate thread access across this call so that no thread intervenes
  870.       // between the calls to stream-size, stream-position, and read-as-r....
  871.       lock-stream(stream);
  872.       let res = read-as-required-vector-count(stream, result-class, #f,
  873.                           (stream.stream-size
  874.                          - stream.stream-position));
  875.       unlock-stream(stream);
  876.       values(res, #t);
  877.     otherwise =>
  878.       error("Count or to-eof? must be supplied to read a <byte-vector>.");
  879.   end;
  880. end method;
  881.  
  882.  
  883.  
  884. /// read-as for <buffer> results from <fd-stream>s.
  885. ///
  886. /// Read-as needs to be implemented for each stream class to avoid double
  887. /// buffering.
  888. ///
  889.  
  890. define method read-as
  891.     (result-class :: singleton(<buffer>), stream :: <fd-stream>,
  892.      #key signal-eof? :: <boolean> = #t,
  893.           count :: false-or(<fixed-integer>),
  894.           to-eof? :: <boolean> = #f)
  895.     => (result :: false-or(<buffer>), eof? :: <boolean>);
  896.   case
  897.     (count) => read-as-buffer-count(stream, result-class, signal-eof?, count);
  898.     (to-eof?) => read-as-required-vector-to-eof(stream, result-class);
  899.     otherwise =>
  900.       error("Count or to-eof? must be supplied to read a buffer.");
  901.   end;
  902. end method;
  903.  
  904. define method read-as-buffer-count
  905.     (stream :: <fd-stream>, result-class :: singleton(<buffer>),
  906.      signal-eof? :: <boolean>, count :: <fixed-integer>)
  907.     => (result :: false-or(<buffer>), eof? :: <boolean>);
  908.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  909.     = get-input-buffer(stream);
  910.   let result = make(<buffer>, size: count);
  911.   let available :: <buffer-index> = (stop - next);
  912.   if (available >= count)
  913.     // All the input we need is already available in the stream's buffer.
  914.     copy-bytes(result, 0, buf, next, count);
  915.     release-input-buffer(stream, next, stop);
  916.     values(result, #f);
  917.   else
  918.     // We need to iterate to get all the input we need.
  919.     // First, copy what is available in the stream's buffer to the result.
  920.     let start = if (available ~= 0)
  921.           copy-bytes(result, 0, buf, next, available);
  922.           available;
  923.         else
  924.           0;
  925.         end;
  926.     let fd = stream.file-descriptor;
  927.     block (exit-loop)
  928.       // Iterate, filling the result buffer directly.
  929.       for (num-bytes :: <buffer-index>
  930.          = call-fd-function(fd-read, fd, result, start, (count - start))
  931.          then call-fd-function(fd-read, fd, result, start,
  932.                    (count - start)),
  933.        until (num-bytes = 0))
  934.     start := start + num-bytes;
  935.     if (start = count)
  936.       release-input-buffer(stream, 0, 0);
  937.       exit-loop(result, #f);
  938.     end;
  939.       finally
  940.     // If we exit normally, then we hit eof.
  941.     release-input-buffer(stream, 0, 0);
  942.     if (signal-eof?)
  943.       error(make(<end-of-file>, stream: stream));
  944.     else
  945.       values(#f, #t);
  946.     end;
  947.       end for;
  948.     end block;
  949.   end if;
  950. end method;
  951.  
  952.  
  953.  
  954. /// read-as for <buffer> results from <fd-file-stream>s.
  955. ///
  956. /// Read-as needs to be implemented for each stream class to avoid double
  957. /// buffering.
  958. ///
  959. /// This method needs to exist even though there is a similar method on
  960. /// <random-access-stream>s because of how applicable methods are sorted.
  961. /// We need to make sure this method executes rather than the one for
  962. /// <fd-stream>s.
  963. ///
  964.  
  965. define method read-as
  966.     (result-class :: singleton(<buffer>), stream :: <fd-file-stream>,
  967.      #key signal-eof? :: <boolean> = #t,
  968.           count :: false-or(<fixed-integer>),
  969.           to-eof? :: <boolean> = #f)
  970.     => (result :: false-or(<buffer>), eof? :: <boolean>);
  971.   case
  972.     (count) =>
  973.       read-as-buffer-count(stream, result-class, signal-eof?, count);
  974.     (to-eof?) =>
  975.       // Isolate thread access across this call so that no thread intervenes
  976.       // between the calls to stream-size, stream-position, and read-as-b....
  977.       lock-stream(stream);
  978.       let res = read-as-buffer-count(stream, result-class, #f,
  979.                      (stream.stream-size
  980.                     - stream.stream-position));
  981.       unlock-stream(stream);
  982.       values(res, #t);
  983.     otherwise =>
  984.       error("Count or to-eof? must be supplied to read a buffer.");
  985.   end;
  986. end method;
  987.  
  988.  
  989.  
  990. /// read-as for <buffer> results from <byte-string-input-stream>s.
  991. ///
  992. /// Read-as needs to be implemented for each stream class to avoid double
  993. /// buffering.
  994. ///
  995.  
  996. define sealed method read-as
  997.     (result-class :: singleton(<buffer>), stream :: <byte-string-input-stream>,
  998.      #key signal-eof? :: <boolean> = #t,
  999.           count :: false-or(<fixed-integer>),
  1000.           to-eof? :: <boolean> = #f)
  1001.     => (result :: false-or(<buffer>), eof? :: <boolean>);
  1002.   case
  1003.     (count) => read-as-buffer-count(stream, result-class, signal-eof?, count);
  1004.     (to-eof?) =>
  1005.       // Isolate thread access across this call so that no thread intervenes
  1006.       // between the calls to stream-size, stream-position, and read-as-b....
  1007.       lock-stream(stream);
  1008.       let res = read-as-buffer-count(stream, result-class, #f,
  1009.                      (stream.stream-size
  1010.                     - stream.stream-position));
  1011.       unlock-stream(stream);
  1012.       values(res, #t);
  1013.     otherwise =>
  1014.       error("Count or to-eof? must be supplied to read a buffer.");
  1015.   end;
  1016. end method;
  1017.  
  1018. define sealed method read-as-buffer-count
  1019.     (stream :: <byte-string-input-stream>, result-class :: singleton(<buffer>),
  1020.      signal-eof? :: <boolean>, count :: <fixed-integer>)
  1021.     => (result :: false-or(<buffer>), eof? :: <boolean>);
  1022.   let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1023.     = get-input-buffer(stream);
  1024.   let available :: <buffer-index> = (stop - next);
  1025.   if (available >= count)
  1026.     let result = make(result-class, size: count);
  1027.     copy-bytes(result, 0, buf, next, count);
  1028.     release-input-buffer(stream, next, stop);
  1029.     values(result, #f);
  1030.   else
  1031.     release-input-buffer(stream, 0, 0);
  1032.     if (signal-eof?)
  1033.       error(make(<end-of-file>, stream: stream));
  1034.     else
  1035.       values(#f, #t);
  1036.     end;
  1037.   end;
  1038. end method;
  1039.  
  1040.  
  1041.  
  1042. /// read-into! for <byte-string>, <byte-vector>, and <buffer> results from <stream>s.
  1043. ///
  1044.  
  1045. define generic read-into!
  1046.     (destination :: <object>, stream :: <stream>,
  1047.      #key signal-eof?: :: <boolean>, // = #t
  1048.           start: :: <fixed-integer>, // = 0,
  1049.           end: :: <fixed-integer>, // = destination.size,
  1050.           to-eof?: :: <boolean>) // = #f)
  1051.     => (result :: false-or(<object>),
  1052.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1053.  
  1054. /// Read-into! for <byte-string>, <byte-vector>, and <buffer> have the same
  1055. /// definition.  There are three "define method" forms so that the distinct
  1056. /// return types can be distinctly declared.  If the "seal generic" form
  1057. /// allowed you to declare return types, there could be one method here
  1058. /// with two "seal generic" forms declaring the distinct specializations
  1059. /// and their associated return types.
  1060. ///
  1061.  
  1062. define sealed method read-into!
  1063.     (destination :: <byte-string>, stream :: <stream>,
  1064.      #key signal-eof? :: <boolean> = #t,
  1065.           start :: <fixed-integer> = 0,
  1066.           end: stop :: <fixed-integer> = destination.size,
  1067.           to-eof? :: <boolean> = #f)
  1068.     => (result :: false-or(<byte-string>),
  1069.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1070.   read-into-required-vector(stream, destination, signal-eof?, to-eof?,
  1071.                 start, stop);
  1072. end method;
  1073.  
  1074. define sealed method read-into!
  1075.     (destination :: <byte-vector>, stream :: <stream>,
  1076.      #key signal-eof? :: <boolean> = #t,
  1077.           start :: <fixed-integer> = 0,
  1078.           end: stop :: <fixed-integer> = destination.size,
  1079.           to-eof? :: <boolean> = #f)
  1080.     => (result :: false-or(<byte-vector>),
  1081.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1082.   read-into-required-vector(stream, destination, signal-eof?, to-eof?,
  1083.                 start, stop);
  1084. end method;
  1085.  
  1086. define sealed method read-into!
  1087.     (destination :: <buffer>, stream :: <stream>,
  1088.      #key signal-eof? :: <boolean> = #t,
  1089.           start :: <fixed-integer> = 0,
  1090.           end: stop :: <fixed-integer> = destination.size,
  1091.           to-eof? :: <boolean> = #f)
  1092.     => (result :: false-or(<buffer>),
  1093.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1094.   read-into-required-vector(stream, destination, signal-eof?, to-eof?,
  1095.                 start, stop);
  1096. end method;
  1097.  
  1098. /// read-into-required-vector -- Internal.
  1099. ///
  1100. /// This function implements read-into! for <byte-string>, <byte-vector>,
  1101. /// and <buffer> for any stream.  There are better methods for <buffer>s on
  1102. /// <fd-stream>s and <byte-string-input-stream>s.
  1103. ///
  1104. define sealed method read-into-required-vector
  1105.     (stream :: <stream>,
  1106.      destination :: type-or(<byte-vector>, <byte-string>, <buffer>),
  1107.      signal-eof? :: <boolean>,
  1108.      to-eof? :: <boolean>,
  1109.      start :: <fixed-integer>,
  1110.      stop :: <fixed-integer>)
  1111.     => (result :: type-or(<byte-vector>, <byte-string>, <buffer>,
  1112.               singleton(#f)),
  1113.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1114.   let (buf :: <buffer>, buf-start :: <buffer-index>, buf-stop :: <buffer-index>)
  1115.     = get-input-buffer(stream);
  1116.   if (buf-start = buf-stop)
  1117.     buf-stop := fill-input-buffer(stream, 0);
  1118.     buf-start := 0;
  1119.   end;
  1120.   block (exit-loop)
  1121.     let available :: <buffer-index> = (buf-stop - buf-start);
  1122.     let count :: <fixed-integer> = available;
  1123.     let stop :: <fixed-integer> = if (to-eof?) destination.size else stop end;
  1124.     let dst-start :: <fixed-integer> = start;
  1125.     for (until (available = 0))
  1126.       let dst-stop :: <fixed-integer> = (dst-start + available);
  1127.       if (dst-stop >= stop)
  1128.     if (to-eof?)
  1129.       error("Destination not big enough to read to EOF -- %=.",
  1130.         destination);
  1131.     end;
  1132.     let this-copy = (stop - dst-start);
  1133.     copy-bytes(destination, dst-start, buf, buf-start, this-copy);
  1134.     release-input-buffer(stream, (buf-start + this-copy),
  1135.                  // Can't assume buf-start is 0 because we may
  1136.                  // come in here on the first iteration.
  1137.                  (buf-start + available));
  1138.     exit-loop(destination, #f);
  1139.       else
  1140.     copy-bytes(destination, dst-start, buf, buf-start, available);
  1141.       end;
  1142.       available := fill-input-buffer(stream, 0);
  1143.       count := (count + available);
  1144.       dst-start := dst-stop;
  1145.       buf-start := 0;
  1146.     finally
  1147.       // Whenever the loop terminates normally, we either successfully read
  1148.       // to EOF, or we failed to read the required data to fill destination
  1149.       // to stop.
  1150.       release-input-buffer(stream, 0, 0);
  1151.       case
  1152.     (to-eof?) => values(destination, (start + count));
  1153.     (signal-eof?) => error(make(<end-of-file>, stream: stream));
  1154.     otherwise => values(#f, #t);
  1155.       end;
  1156.     end for;
  1157.   end block;
  1158. end method;
  1159.  
  1160.  
  1161.  
  1162. /// read-into! for <buffer> destinations on <fd-stream>s and <byte-string-input-stream>s.
  1163. ///
  1164. /// This page contains read-into! methods that fill <buffer>s for
  1165. /// <fd-stream>s and <byte-string-input-stream>s.  Read-into! for <buffer>s
  1166. /// needs to be implemented for each stream class to avoid double
  1167. /// buffering.
  1168. ///
  1169.  
  1170. define sealed method read-into!
  1171.     (destination :: <buffer>, stream :: <fd-stream>,
  1172.      #key signal-eof? :: <boolean> = #t,
  1173.           start :: <buffer-index> = 0,
  1174.           end: stop :: <buffer-index> = destination.size,
  1175.           to-eof? :: <boolean> = #f)
  1176.     => (result :: false-or(<buffer>),
  1177.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1178.   let (buf :: <buffer>, buf-start :: <buffer-index>, buf-stop :: <buffer-index>)
  1179.     = get-input-buffer(stream);
  1180.   let (start :: <buffer-index>, count :: <buffer-index>)
  1181.     = if (buf-start = buf-stop)
  1182.     values(start, 0);
  1183.       else
  1184.     let count = min((buf-stop - buf-start), (stop - start));
  1185.     copy-bytes(destination, start, buf, buf-start, count);
  1186.     values((start + count), count);
  1187.       end;
  1188.   let fd = stream.file-descriptor;
  1189.   block (exit-loop)
  1190.     let stop :: <buffer-index> = if (to-eof?) destination.size else stop end;
  1191.     for (num-bytes :: <buffer-index>
  1192.        = call-fd-function(fd-read, fd, destination, start, (stop - start))
  1193.      then call-fd-function(fd-read, fd, destination, start,
  1194.                    (stop - start)),
  1195.      until (num-bytes = 0))
  1196.       start := start + num-bytes;
  1197.       count := count + num-bytes;
  1198.       case
  1199.     (start ~= stop) =>
  1200.       // Keep going and try to get more input.
  1201.       #f;   // Case is broken in Mindy.
  1202.     (~ to-eof?) =>
  1203.       // We got all the requested input, and we are not trying to read to
  1204.       // EOF.  Just return everything.
  1205.       release-input-buffer(stream, 0, 0);
  1206.       exit-loop(destination, #f);
  1207.     (call-fd-function(fd-read, fd, buf, 0, buf.size) ~= 0) =>
  1208.       // We're trying to read to EOF, and we've read everything the buffer
  1209.       // can hold.  Furthermore, there is still input available, so error.
  1210.       error("Destination not big enough to read to EOF -- %=.",
  1211.         destination);
  1212.     otherwise =>
  1213.       // Everything's cool.  Return successfully.
  1214.       release-input-buffer(stream, 0, 0);
  1215.       exit-loop(destination, count);
  1216.       end;
  1217.     finally
  1218.       // Whenever the loop terminates normally, we either successfully read
  1219.       // to EOF, or we failed to read the required data to fill the
  1220.       // destination to stop.
  1221.       release-input-buffer(stream, 0, 0);
  1222.       case
  1223.     (to-eof?) => values(destination, count);
  1224.     (signal-eof?) => error(make(<end-of-file>, stream: stream));
  1225.     otherwise => values(#f, #t);
  1226.       end;
  1227.     end for;
  1228.   end block;
  1229. end method;
  1230.  
  1231. define sealed method read-into!
  1232.     (destination :: <buffer>, stream :: <byte-string-input-stream>,
  1233.      #key signal-eof? :: <boolean> = #t,
  1234.           start :: <buffer-index> = 0,
  1235.           end: stop :: <buffer-index> = destination.size,
  1236.           to-eof? :: <boolean>)
  1237.     => (result :: false-or(<buffer>),
  1238.     eof?-or-how-much :: union(<boolean>, <fixed-integer>));
  1239.   let (buf :: <buffer>, next :: <buffer-index>, buf-stop :: <buffer-index>)
  1240.     = get-input-buffer(stream);
  1241.   let available :: <buffer-index> = (buf-stop - next);
  1242.   if (to-eof?)
  1243.     if (available <= (destination.size - start))
  1244.       copy-bytes(destination, start, buf, next, available);
  1245.       values(destination, available);
  1246.     else
  1247.       release-input-buffer(stream, next, buf-stop);
  1248.       error("Destination not big enough to read to EOF -- %=.",
  1249.         destination);
  1250.     end;
  1251.   else
  1252.     let need :: <buffer-index> = (stop - start);
  1253.     if (available >= need)
  1254.       copy-bytes(destination, 0, buf, next, need);
  1255.       release-input-buffer(stream, next, buf-stop);
  1256.       values(destination, #f);
  1257.     else
  1258.       release-input-buffer(stream, 0, 0);
  1259.       if (signal-eof?)
  1260.     error(make(<end-of-file>, stream: stream));
  1261.       else
  1262.     values(#f, #t);
  1263.       end;
  1264.     end;
  1265.   end;
  1266. end method;
  1267.  
  1268.  
  1269.  
  1270. /// write
  1271. ///
  1272.  
  1273. define generic write (object :: <object>, stream :: <stream>, #key)
  1274.     => stream :: <stream>;
  1275.  
  1276.  
  1277. define sealed method write (object :: <byte-character>, stream :: <stream>,
  1278.                 #key)
  1279.     => stream :: <stream>;
  1280.   let (buf :: <buffer>, next :: <buffer-index>, size :: <buffer-index>)
  1281.     = get-output-buffer(stream);
  1282.   if (next = size)
  1283.     empty-output-buffer(stream, size);
  1284.     next := 0;
  1285.   end;
  1286.   buf[next] := as(<byte>, object);
  1287.   release-output-buffer(stream, next + 1);
  1288.   stream;
  1289. end method;
  1290.  
  1291. define sealed method write (object :: <byte>, stream :: <stream>, #key)
  1292.     => stream :: <stream>;
  1293.   let (buf :: <buffer>, next :: <buffer-index>, size :: <buffer-index>)
  1294.     = get-output-buffer(stream);
  1295.   if (next = size)
  1296.     empty-output-buffer(stream, size);
  1297.     next := 0;
  1298.   end;
  1299.   buf[next] := object;
  1300.   release-output-buffer(stream, next + 1);
  1301.   stream;
  1302. end method;
  1303.  
  1304. /// This method implements the write function for <byte-string> and
  1305. /// <byte-vector>.  This function would work for <buffer>s too, but writing
  1306. /// buffers is implemented for each stream individually to avoid double
  1307. /// buffer.
  1308. ///
  1309. define sealed method write (object :: type-or(<byte-vector>, <byte-string>),
  1310.                 stream :: <stream>,
  1311.                 #key start :: <fixed-integer> = 0,
  1312.                      end: stop :: <fixed-integer> = object.size)
  1313.     => stream :: <stream>;
  1314.   let (buf :: <buffer>, next :: <buffer-index>, size :: <buffer-index>)
  1315.     = get-output-buffer(stream);
  1316.   if (next = size)
  1317.     empty-output-buffer(stream, size);
  1318.     next := 0;
  1319.   end;
  1320.   block (exit-loop)
  1321.     let buf-capacity :: <buffer-index> = (size - next);
  1322.     let buf-start :: <buffer-index> = next;
  1323.     while (#t)
  1324.       let partial-stop :: <fixed-integer> = (start + buf-capacity);
  1325.       if (partial-stop >= stop)
  1326.     let this-copy = (stop - start);
  1327.     copy-bytes(buf, buf-start, object, start, this-copy);
  1328.     release-output-buffer(stream, (buf-start + this-copy));
  1329.     exit-loop(stream);
  1330.       else
  1331.     copy-bytes(buf, buf-start, object, start, buf-capacity);
  1332.       end;
  1333.       empty-output-buffer(stream, size);
  1334.       buf-capacity := size;
  1335.       buf-start := 0;
  1336.       start := partial-stop;
  1337.     end;
  1338.   end block;
  1339.   stream;
  1340. end method;
  1341.  
  1342. // Mindy does not parse "seal generic" forms currently.
  1343. // The streams spec requires sealed methods for these types.
  1344. //
  1345. // seal generic write (<byte-vector>, <stream>);
  1346. // seal generic write (<byte-string>, <stream>);
  1347. //
  1348.  
  1349.  
  1350.  
  1351. /// write for <buffer>s.
  1352. ///
  1353. /// This page contains implementations of write for each stream type so that
  1354. /// writing buffers can avoid double buffering.
  1355. ///
  1356.  
  1357. define sealed method write (object :: <buffer>, stream :: <fd-stream>,
  1358.                 #key start :: <fixed-integer> = 0,
  1359.                      end: stop :: <fixed-integer> = object.size)
  1360.     => stream :: <stream>;
  1361.   let (buf :: <buffer>, next :: <buffer-index>)
  1362.     = get-output-buffer(stream);
  1363.   if (next ~= 0)
  1364.     empty-output-buffer(stream, next);
  1365.   end;
  1366.   let fd = stream.file-descriptor;
  1367.   let buf = stream.buffer;
  1368.   // Keep writing until fd-write manages to write everything.
  1369.   for (x :: <buffer-index>
  1370.      = (start + call-fd-function(fd-write, fd, object, start, stop))
  1371.          then (x + call-fd-function(fd-write, fd, buf, x, stop - x)),
  1372.        until (x = stop))
  1373.   end;
  1374.   release-output-buffer(stream, 0);
  1375.   stream;
  1376. end method;
  1377.  
  1378. define sealed method write
  1379.     (object :: <buffer>, stream :: <byte-string-output-stream>,
  1380.      #key start :: <fixed-integer> = 0,
  1381.           end: stop :: <fixed-integer> = object.size)
  1382.     => stream :: <stream>;
  1383.   let (buf :: <buffer>, buf-stop :: <buffer-index>)
  1384.     = get-output-buffer(stream);
  1385.   let object-len :: <fixed-integer> = (stop - start);
  1386.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  1387.   if (backup)
  1388.     // Collect all output into a new backup.
  1389.     let backup-len :: <fixed-integer> = backup.size;
  1390.     let new-backup-len = backup-len + object-len + buf-stop;
  1391.     let new-backup :: <byte-string>
  1392.       = make(<byte-string>, size: new-backup-len);
  1393.     copy-bytes(new-backup, 0, backup, 0, backup-len);
  1394.     let backup-and-buf-len = (backup-len + buf-stop);
  1395.     if (buf-stop ~= 0)
  1396.       copy-bytes(new-backup, backup-len, buf, 0, buf-stop);
  1397.     end;
  1398.     copy-bytes(new-backup, backup-and-buf-len, object, start, object-len);
  1399.     stream.string-output-stream-backup := new-backup;
  1400.   else
  1401.     // Collect any output into a backup and leave the stream's buffer empty.
  1402.     let backup-len = object-len + buf-stop;
  1403.     let backup :: <byte-string>
  1404.       = make(<byte-string>, size: backup-len);
  1405.     if (buf-stop ~= 0)
  1406.       copy-bytes(backup, 0, buf, 0, buf-stop);
  1407.     end;
  1408.     copy-bytes(backup, buf-stop, object, start, object-len);
  1409.     stream.string-output-stream-backup := backup;
  1410.   end;
  1411.   release-output-buffer(stream, 0);
  1412.   stream;
  1413. end method;
  1414.  
  1415.  
  1416.  
  1417. /// write-line
  1418. ///
  1419.  
  1420. define generic write-line (object :: <object>, stream :: <stream>, #all-keys)
  1421.     => stream :: <stream>;
  1422.  
  1423.  
  1424. define method write-line (object :: <object>, stream :: <stream>,
  1425.               #rest key-args, #all-keys)
  1426.     => stream :: <stream>;
  1427.   lock-stream(stream);
  1428.   apply(write, object, stream, key-args);
  1429.   write('\n', stream);
  1430.   unlock-stream(stream);
  1431.   stream;
  1432. end method;
  1433.  
  1434.  
  1435.  
  1436. //// Fd Streams -- class definition and Stream Extension Protocol.
  1437. ///
  1438.  
  1439. /// <fd-stream> Class -- Exported.
  1440. ///
  1441. /// All file descriptor based streams inherit from this class.
  1442. ///
  1443. /// This is a non-standard class defined for Gwydion streams.  This stream
  1444. /// and <file-stream> are the superclasses of <fd-file-stream>s.
  1445. ///
  1446. define class <fd-stream> (<stream>)
  1447.   //
  1448.   // This slot holds the direction of the file-descriptor.  <fd-stream>s have
  1449.   // a single direction, as presented to the user.  However, if the file
  1450.   // descriptor really refers to a file, then the <fd-stream> is actually
  1451.   // bidirectional.  For <fd-stream>s, this slot is used to enforce the
  1452.   // direction specified when making the stream.  For <fd-file-stream>s,
  1453.   // this slot indicates the direction the user last used the stream, and the
  1454.   // value of this slot changes as the user changes directions of the
  1455.   // <fd-file-stream>.
  1456.   slot fd-direction :: one-of(#"input", #"output");
  1457.   slot file-descriptor :: <integer>;
  1458.   //
  1459.   // This slot has a buffer when the stream is open, #f when closed.
  1460.   slot buffer :: false-or(<buffer>);
  1461.   //
  1462.   // Buffer-next for input: streams holds the next available byte for input.
  1463.   // For output: streams this slot holds the next available location for
  1464.   // placing output.
  1465.   slot buffer-next :: <buffer-index>;
  1466.   //
  1467.   // Buffer-stop for input: streams holds the end of the available input.
  1468.   // This slot holds no meaningful value for output: streams.
  1469.   slot buffer-stop :: <buffer-index>;
  1470. end class;
  1471.  
  1472. define sealed method close (stream :: <fd-stream>) => ();
  1473.   if (stream.fd-direction == #"input")
  1474.     // Get buffer to make sure no one holds it.
  1475.     get-input-buffer(stream);
  1476.     call-fd-function(fd-close, stream.file-descriptor);
  1477.     stream.buffer := #f;
  1478.     release-input-buffer(stream, 0, 0);
  1479.   else
  1480.     let (buf :: <buffer>, next :: <buffer-index>)
  1481.       =    get-output-buffer(stream);
  1482.     if (next ~= 0)
  1483.       empty-output-buffer(stream, next);
  1484.     end;
  1485.     synchronize(stream);
  1486.     call-fd-function(fd-close, stream.file-descriptor);
  1487.     stream.buffer := #f;
  1488.     unregister-output-stream(stream);
  1489.     release-output-buffer(stream, 0);
  1490.   end;
  1491. end method;
  1492.   
  1493. define sealed method initialize
  1494.     (stream :: <fd-stream>, #next next-method,
  1495.      #key direction :: one-of(#"input", #"output") = #"input",
  1496.           fd :: <integer>,
  1497.           size: length :: <buffer-index> = $default-buffer-size)
  1498.     => result :: <fd-stream>;
  1499.   next-method();
  1500.   stream.fd-direction := direction;
  1501.   stream.file-descriptor := fd;
  1502.   stream.buffer := make(<buffer>, size: length);
  1503.   if (direction == #"input")
  1504.     // Next and stop are the same so that the first read will fill the buffer.
  1505.     stream.buffer-next := (stream.buffer-stop := 0);
  1506.   else
  1507.     register-output-stream(stream);
  1508.     stream.buffer-next := 0;
  1509.   end;
  1510.   stream;
  1511. end method;
  1512.  
  1513. define sealed method stream-extension-get-input-buffer
  1514.     (stream :: <fd-stream>)
  1515.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  1516.   let direction = stream.fd-direction;
  1517.   if (direction == #"output")
  1518.     error("Stream is an output stream -- %=.", stream);
  1519.   end;
  1520.   let buf = stream.buffer;
  1521.   // Since buffer is currently unheld by anyone, make sure it isn't closed.
  1522.   if (~ buf) error("Stream has been closed -- %=.", stream) end;
  1523.   values(buf, stream.buffer-next, stream.buffer-stop);
  1524. end method;
  1525.  
  1526. define sealed method stream-extension-release-input-buffer
  1527.     (stream :: <fd-stream>, next :: <buffer-index>, stop :: <buffer-index>)
  1528.     => ();
  1529.   let direction = stream.fd-direction;
  1530.   case
  1531.     (direction == #"output") =>
  1532.       error("Stream is an output stream -- %=.", stream);
  1533.     (stop < next) =>
  1534.       error("Returned buffer with stop, %d, less than next, %d.", stop, next);
  1535.     otherwise =>
  1536.       stream.buffer-next := next;
  1537.       stream.buffer-stop := stop;
  1538.   end;
  1539. end method;
  1540.  
  1541. define sealed method stream-extension-fill-input-buffer
  1542.     (stream :: <fd-stream>, start :: <buffer-index>)
  1543.     => stop :: <buffer-index>;
  1544.   let direction = stream.fd-direction;
  1545.   if (direction == #"output")
  1546.     error("Stream is an output stream -- %=.", stream);
  1547.   end;
  1548.   let buf = stream.buffer;
  1549.   let count = call-fd-function(fd-read, stream.file-descriptor, buf,
  1550.                    start, (buf.size - start));
  1551.   // Don't bother updating stream's notion of next and stop because we
  1552.   // rely on what the users tell us when they return the buffer.  Just
  1553.   // return the value.
  1554.   if (count = 0)
  1555.     0;
  1556.   else
  1557.     start + count;
  1558.   end;
  1559. end method;
  1560.  
  1561. define sealed method stream-extension-input-available-at-source?
  1562.     (stream :: <fd-stream>)
  1563.     => available? :: <boolean>;
  1564.   let direction = stream.fd-direction;
  1565.   if (direction == #"output")
  1566.     error("Stream is an output stream -- %=.", stream);
  1567.   end;
  1568.   call-fd-function(fd-input-available?, stream.file-descriptor);
  1569. end method;
  1570.  
  1571. define sealed method stream-extension-get-output-buffer
  1572.     (stream :: <fd-stream>)
  1573.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  1574.   let direction = stream.fd-direction;
  1575.   if (direction == #"input")
  1576.     error("Stream is an input stream -- %=.", stream);
  1577.   end;
  1578.   let buf = stream.buffer;
  1579.   // Since no one holds the buffer, make sure the stream isn't closed.
  1580.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  1581.   let next :: <buffer-index> = stream.buffer-next;
  1582.   let buf-size :: <buffer-index> = buf.size;
  1583.   if (next = buf-size)
  1584.     let fd = stream.file-descriptor;
  1585.     // Keep writing until fd-write manages to write everything.
  1586.     for (x :: <buffer-index>
  1587.         = call-fd-function(fd-write, fd, buf, 0, next)
  1588.         then (x + call-fd-function(fd-write, fd, buf, x, next - x)),
  1589.      until (x = next))
  1590.     end;
  1591.     values(buf, 0, buf-size)
  1592.   else
  1593.     values(buf, next, buf-size);
  1594.   end;
  1595. end method;
  1596.  
  1597. define sealed method stream-extension-release-output-buffer
  1598.     (stream :: <fd-stream>, next :: <buffer-index>)
  1599.     => ();
  1600.   let direction = stream.fd-direction;
  1601.   if (direction == #"input")
  1602.     error("Stream is an input stream -- %=.", stream);
  1603.   end;
  1604.   stream.buffer-next := next;
  1605. end method;
  1606.  
  1607. define sealed method stream-extension-empty-output-buffer
  1608.     (stream :: <fd-stream>, stop :: <buffer-index>)
  1609.     => ();
  1610.   if (stream.fd-direction == #"input")
  1611.     error("Stream is an input stream -- %=.", stream);
  1612.   end;
  1613.   let fd = stream.file-descriptor;
  1614.   let buf = stream.buffer;
  1615.   // Keep writing until fd-write manages to write everything.
  1616.   for (x :: <buffer-index> = call-fd-function(fd-write, fd, buf, 0, stop)
  1617.          then (x + call-fd-function(fd-write, fd, buf, x, stop - x)),
  1618.        until (x = stop))
  1619.   end;
  1620. end;
  1621.  
  1622. define sealed method stream-extension-synchronize (stream :: <fd-stream>)
  1623.     => ();
  1624.   call-fd-function(fd-sync-output, stream.file-descriptor);
  1625. end;
  1626.  
  1627.  
  1628.  
  1629. //// Random Access Streams --  generic function declarations.
  1630. ////
  1631.  
  1632. /// All of these are exported.
  1633. ///
  1634.  
  1635. define generic stream-position (stream :: <random-access-stream>)
  1636.     => position :: <integer>;
  1637.  
  1638. define generic stream-position-setter
  1639.     (position :: <integer>, stream :: <random-access-stream>)
  1640.     => position :: <integer>;
  1641.  
  1642. define generic adjust-stream-position
  1643.     (offset :: <integer>,
  1644.      stream :: <random-access-stream>,
  1645.      #key from: :: one-of(#"start", #"current", #"end")) // = #"start"
  1646.     => position :: <integer>;
  1647.  
  1648. define generic stream-size (stream :: <random-access-stream>)
  1649.     => size :: <integer>;
  1650.  
  1651.  
  1652.  
  1653. //// Fd File Streams -- class declarations and Random Access Protocol.
  1654. ////
  1655.  
  1656. /// <file-stream> Class -- Exported.
  1657. ///
  1658. define abstract class <file-stream> (<random-access-stream>)
  1659. end class;
  1660.  
  1661. /// <fd-file-stream> Class -- Internal.
  1662. ///
  1663. /// This is the concrete class that is instantiated when users make a
  1664. /// <file-stream>.
  1665. ///
  1666. define sealed class <fd-file-stream> (<fd-stream>, <file-stream>)
  1667.   slot file-name :: <byte-string>;
  1668.   slot file-direction :: one-of(#"input", #"output", #"input-output");
  1669. end class;
  1670.  
  1671.  
  1672. /// stream-position -- Method for Exported Interface.
  1673. ///
  1674. define sealed method stream-position (stream :: <fd-file-stream>)
  1675.     => position :: <integer>;
  1676.   if (stream.file-direction == #"input")
  1677.     // Get the buffer to ensure no one else is using it and to make it
  1678.     // possible to correctly compute the actual file position.
  1679.     let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  1680.       = get-input-buffer(stream);
  1681.     ignore(buf);
  1682.     // Get the current position as recorded by the file-descritor module
  1683.     // and subtract what input we have in the buffer but haven't actually
  1684.     // read.
  1685.     let pos = (fd-seek(stream.file-descriptor, 0, fd-seek-current)
  1686.          - (stop - next));
  1687.     release-input-buffer(stream, next, stop);
  1688.     pos;
  1689.   else
  1690.     // Direction is #"output" or #"input-output".
  1691.     // Get the buffer to ensure no one else is using it and to make it
  1692.     // possible to correctly compute the actual file position.
  1693.     let (buf, next :: <buffer-index>, stop)
  1694.       = get-output-buffer(stream);
  1695.     ignore(buf, stop);
  1696.     // Get the current position as recorded by the file-descritor module
  1697.     // and add what output we have in the buffer but haven't sent yet.
  1698.     let pos = fd-seek(stream.file-descriptor, 0, fd-seek-current) + next;
  1699.     release-output-buffer(stream, next);
  1700.     pos;
  1701.   end;
  1702. end method;
  1703.  
  1704. /// stream-position-setter -- Method for Exported Interface.
  1705. ///
  1706. define sealed method stream-position-setter
  1707.     (position :: <integer>, stream :: <fd-file-stream>)
  1708.     => position :: <integer>;
  1709.   let direction = file-direction(stream);
  1710.   // Get the buffer to ensure no one else is using it and to make it
  1711.   // possible to invalidate the buffer's contents.
  1712.   if (direction == #"input")
  1713.     get-input-buffer(stream);
  1714.   else
  1715.     let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1716.       = get-output-buffer(stream);
  1717.     ignore(buf, stop);
  1718.     // Force any pending output so that we can later correctly test for the
  1719.     // file's size.
  1720.     if (next > 0)
  1721.       empty-output-buffer(stream, next);
  1722.     end;
  1723.   end;
  1724.   // Set the position.
  1725.   let fd = stream.file-descriptor;
  1726.   if ((position > 0) &
  1727.       (position < call-fd-function(fd-seek, fd, 0, fd-seek-end)))
  1728.     call-fd-function(fd-seek, fd, position, fd-seek-start);
  1729.   else
  1730.     error("Illegal stream position -- %d", position);
  1731.   end;
  1732.   // Cleanup.
  1733.   if (direction == #"input")
  1734.     release-input-buffer(stream, 0, 0);
  1735.   else
  1736.     release-output-buffer(stream, 0);
  1737.   end;
  1738.   position;
  1739. end method;
  1740.  
  1741. /// adjust-stream-position -- Method for Exported Interface.
  1742. ///
  1743. define sealed method adjust-stream-position
  1744.     (offset :: <integer>, stream :: <fd-file-stream>, 
  1745.      #key from: reference :: one-of(#"start", #"current", #"end") = #"start")
  1746.     => position :: <integer>;
  1747.   let direction = file-direction(stream);
  1748.   if (direction == #"input")
  1749.     // Get the buffer to ensure no one else is using it and to make it
  1750.     // possible to invalidate the buffer's contents.
  1751.     let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1752.       = get-input-buffer(stream);
  1753.     ignore(buf);
  1754.     if (reference == #"current")
  1755.       // If moving the position relative to the current position, then
  1756.       // adjust the offset to account for the unread input in the buffer.
  1757.       // Because of the unread input, the file-descriptor module's record
  1758.       // of the position is ahead of the actual position.
  1759.       offset := offset - (stop - next);
  1760.     end;
  1761.     let pos = call-fd-function(fd-seek, stream.file-descriptor, offset,
  1762.                    select (reference)
  1763.                  (#"start") => fd-seek-start;
  1764.                  (#"current") => fd-seek-current;
  1765.                  (#"end") => fd-seek-end;
  1766.                    end);
  1767.     release-input-buffer(stream, 0, 0);
  1768.     pos;
  1769.   else
  1770.     // Get the buffer to ensure no one else is using it and to make it
  1771.     // possible to invalidate the buffer's contents.
  1772.     let (buf :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>)
  1773.       = get-output-buffer(stream);
  1774.     ignore(buf, stop);
  1775.     // Force out any pending output while the file position is still right
  1776.     // for the file to receive this output.
  1777.     if (next > 0)
  1778.       empty-output-buffer(stream, next);
  1779.     end;
  1780.     let pos = call-fd-function(fd-seek, stream.file-descriptor, offset,
  1781.                    select (reference)
  1782.                  (#"start") => fd-seek-start;
  1783.                  (#"current") => fd-seek-current;
  1784.                  (#"end") => fd-seek-end;
  1785.                    end);
  1786.     release-output-buffer(stream, 0);
  1787.     pos;
  1788.   end;
  1789. end method;
  1790.  
  1791. /// stream-size -- Method for Exported Interface.
  1792. ///
  1793. define sealed method stream-size (stream :: <fd-file-stream>)
  1794.     => size :: <integer>;
  1795.   if (stream.file-direction == #"input")
  1796.     // Get the buffer to ensure no one else is using it and to make it
  1797.     // possible to correctly compute the actual file position.
  1798.     let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  1799.       = get-input-buffer(stream);
  1800.     ignore(buf);
  1801.     let pos = fd-seek(stream.file-descriptor, 0, fd-seek-current);
  1802.     let size = fd-seek(stream.file-descriptor, 0, fd-seek-end);
  1803.     fd-seek(stream.file-descriptor, pos, fd-seek-start);
  1804.     release-input-buffer(stream, next, stop);
  1805.     size;
  1806.   else
  1807.     // Direction is #"output" or #"input-output".
  1808.     // Get the buffer to ensure no one else is using it and to make it
  1809.     // possible to correctly compute the actual file position and size.
  1810.     let (buf, next :: <buffer-index>, stop)
  1811.       = get-output-buffer(stream);
  1812.     ignore(buf, stop);
  1813.     // Force any pending output so that we can later correctly test for the
  1814.     // file's size.  We don't know if the current pending output is
  1815.     // overwriting part of the file or extending its length.
  1816.     if (next > 0)
  1817.       empty-output-buffer(stream, next);
  1818.     end;
  1819.     let pos = fd-seek(stream.file-descriptor, 0, fd-seek-current);
  1820.     let size = fd-seek(stream.file-descriptor, 0, fd-seek-end);
  1821.     fd-seek(stream.file-descriptor, pos, fd-seek-start);
  1822.     release-output-buffer(stream, next);
  1823.     size;
  1824.   end;
  1825. end method;
  1826.  
  1827.  
  1828.  
  1829. //// Fd File Streams -- Stream Extension Protocol.
  1830. ////
  1831.  
  1832. /// The following methods from <fd-streams> work:
  1833. ///    close
  1834. ///    stream-extension-synchronize
  1835. ///
  1836.  
  1837.  
  1838. /// file-buffer-last-use -- Internal.
  1839. /// file-buffer-last-use-setter -- Internal.
  1840. ///
  1841. /// These are defined for readability.
  1842. ///
  1843. define constant file-buffer-last-use = fd-direction;
  1844. define constant file-buffer-last-use-setter = fd-direction-setter;
  1845.  
  1846.  
  1847. define method make (result-class :: singleton(<file-stream>), #rest keys,
  1848.             #all-keys)
  1849.     => result :: <fd-file-stream>;
  1850.   apply(make, <fd-file-stream>, keys);
  1851. end method;
  1852.  
  1853. define sealed method initialize
  1854.     (stream :: <fd-file-stream>, #next next-method, #rest rest-args,
  1855.      #key name :: false-or(<byte-string>),
  1856.           direction :: one-of(#"input", #"output", #"input-output")
  1857.                      = #"input",
  1858.           if-exists :: one-of(#"signal", #"replace", #"overwrite",
  1859.                   #"append")
  1860.                  = #"replace",
  1861.       size: length :: <buffer-index> = $default-buffer-size)
  1862.     => result :: <fd-file-stream>;
  1863.   if (~ name)
  1864.     error("Must supply a filename when making a <file-stream>.");
  1865.   end;
  1866.   if (direction == #"input")
  1867.     let (fd, err) = fd-open(name, fd-o_rdonly);
  1868.     case
  1869.       (~ err) => #f;   // Case is broken in Mindy.
  1870.       (err = fd-enoent) => error(make(<file-not-found>, filename: name));
  1871.       // Do not pass error string directly because it might have something
  1872.       // that looks like a control-string directive.
  1873.       otherwise => error("%S", fd-error-string(err))
  1874.     end;
  1875.     stream.file-name := name;
  1876.     stream.file-direction := #"input";
  1877.     apply(next-method, stream, fd: fd, direction: #"input", rest-args); 
  1878.     stream;
  1879.   else
  1880.     // Make an #"output" or #"input-output" stream.
  1881.     let flags :: <integer> = fd-o_creat;
  1882.     flags := select (direction)
  1883.            (#"output") => logior(flags, fd-o_wronly);
  1884.            (#"input-output") => logior(flags, fd-o_rdwr);
  1885.          end;
  1886.     flags := select (if-exists)
  1887.            (#"signal") => logior(flags, fd-o_excl);
  1888.            (#"replace") => logior(flags, fd-o_trunc);
  1889.            otherwise => flags;
  1890.          end;
  1891.     let (fd, err) = fd-open(name, flags);
  1892.     case
  1893.       (~ err) => #f;   // Case is broken in Mindy.
  1894.       (err = fd-eexist) => error(make(<file-exists>, filename: name));
  1895.       // Do not pass error string directly because it might have something
  1896.       // that looks like a control-string directive.
  1897.       otherwise => error("%S", fd-error-string(err))
  1898.     end;
  1899.     if (if-exists == #"append")
  1900.       call-fd-function(fd-seek, fd, 0, fd-seek-end);
  1901.     end;
  1902.     stream.file-name := name;
  1903.     stream.file-direction := direction;
  1904.     apply(next-method, stream, fd: fd,
  1905.       direction: if (direction == #"output") #"output" else #"input" end,
  1906.       rest-args); 
  1907.     register-output-stream(stream);
  1908.   end;
  1909. end method;
  1910.  
  1911. define sealed method close (stream :: <fd-file-stream>, #next next-method)
  1912.     => ();
  1913.   next-method();
  1914.   if ((stream.file-direction == #"input-output")
  1915.     & (stream.file-buffer-last-use == #"input"))
  1916.     unregister-output-stream(stream);
  1917.   end;
  1918. end method;
  1919.  
  1920. /// This method does not call next-method because this method does most of the
  1921. /// work determining what to do, and if it did call next-method, in one case
  1922. /// it would have to do extra work just to make next-method work.
  1923. ///
  1924. /// This method does not have to check whether the stream or buffer is locked
  1925. /// because get-input-buffer does that.
  1926. /// 
  1927. define sealed method stream-extension-get-input-buffer
  1928.     (stream :: <fd-file-stream>)
  1929.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  1930.   let direction = stream.file-direction;
  1931.   if (direction == #"output")
  1932.     error("Stream is an output stream -- %=.", stream);
  1933.   end;
  1934.   let buf = stream.buffer;
  1935.   // Since buffer is currently unheld by anyone, make sure it isn't closed.
  1936.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  1937.   if ((direction == #"input") | (stream.file-buffer-last-use == #"input"))
  1938.     values(buf, stream.buffer-next, stream.buffer-stop);
  1939.   else
  1940.     // The stream is both #"input-output" and was last used for #"output".
  1941.     let next :: <buffer-index> = stream.buffer-next;
  1942.     if (next > 0)
  1943.       // Keep writing until fd-write manages to write everything.
  1944.       let fd = stream.file-descriptor;
  1945.       for (x :: <buffer-index>
  1946.          = call-fd-function(fd-write, fd, buf, 0, next)
  1947.          then (x + call-fd-function(fd-write, fd, buf, x, next - x)),
  1948.        until (x = next))
  1949.       end;
  1950.     end;
  1951.     stream.file-buffer-last-use := #"input";
  1952.     // There's no reason to update the stream's notion of next and stop
  1953.     // because we rely on the users' values when they return the buffer.
  1954.     values(buf, 0, 0);
  1955.   end;
  1956. end method;
  1957.  
  1958. /// This method does not call next-method because this method does most of the
  1959. /// work determining what to do, and then only sets two slots.
  1960. ///
  1961. /// This method does not have to check whether the stream or buffer is locked
  1962. /// because release-input-buffer does that.
  1963. ///
  1964. define sealed method stream-extension-release-input-buffer
  1965.     (stream :: <fd-file-stream>, next :: <buffer-index>, stop :: <buffer-index>)
  1966.     => ();
  1967.   let direction = stream.file-direction;
  1968.   case (direction == #"output") =>
  1969.       error("Stream is an output stream -- %=.", stream);
  1970.     (~ ((direction == #"input") |
  1971.     (stream.file-buffer-last-use == #"input"))) =>
  1972.       error("Buffer is currently held for output -- %=.", stream);
  1973.     (stop < next) =>
  1974.       error("Returned buffer with stop, %d, less than next, %d.", stop, next);
  1975.     otherwise =>
  1976.       stream.buffer-next := next;
  1977.       stream.buffer-stop := stop;
  1978.   end;
  1979. end method;
  1980.  
  1981. /// This method does not call next-method because it would waste time doing
  1982. /// some tests again and then only execute a few statements.
  1983. ///
  1984. define sealed method stream-extension-fill-input-buffer
  1985.     (stream :: <fd-file-stream>, start :: <buffer-index>)
  1986.     => stop :: <buffer-index>;
  1987.   let direction = stream.file-direction;
  1988.   if (direction == #"output")
  1989.     error("Stream is an output stream -- %=.", stream);
  1990.   end;
  1991.   if ((direction == #"input") | (stream.file-buffer-last-use == #"input"))
  1992.     let buf = stream.buffer;
  1993.     let count = call-fd-function(fd-read, stream.file-descriptor, buf,
  1994.                  start, (buf.size - start));
  1995.     // Don't bother updating stream's notion of next and stop because we
  1996.     // rely on what the users tell us when they return the buffer.  Just
  1997.     // return the value.
  1998.     if (count = 0)
  1999.       0;
  2000.     else
  2001.       start + count;
  2002.     end;
  2003.   else
  2004.     error("Buffer is currently held for output -- %=.", stream);
  2005.   end;
  2006. end method;
  2007.  
  2008. /// This method does not call next-method because it would waste time doing
  2009. /// some tests again and then only execute one line.
  2010. ///
  2011. define sealed method stream-extension-input-available-at-source?
  2012.     (stream :: <fd-file-stream>)
  2013.     => available? :: <boolean>;
  2014.   let direction = stream.file-direction;
  2015.   if (direction == #"output")
  2016.     error("Stream is an output stream -- %=.", stream);
  2017.   end;
  2018.   if ((direction == #"input") | (stream.file-buffer-last-use == #"input"))
  2019.     call-fd-function(fd-input-available?, stream.file-descriptor);
  2020.   else
  2021.     error("Buffer is currently held for output -- %=.", stream);
  2022.   end;
  2023. end method;
  2024.  
  2025. /// This method does not call next-method because this method does most of the
  2026. /// work determining what to do, and if it did call next-method, in one case
  2027. /// it would have to do extra work just to make next-method work.
  2028. /// 
  2029. /// This method does not have to check whether the stream or buffer is locked
  2030. /// because get-output-buffer does that.
  2031. ///
  2032. define sealed method stream-extension-get-output-buffer
  2033.     (stream :: <fd-file-stream>)
  2034.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  2035.   let direction = stream.file-direction;
  2036.   if (direction == #"input")
  2037.     error("Stream is an input stream -- %=.", stream);
  2038.   end;
  2039.   let buf = stream.buffer;
  2040.   // Since buffer is unheld by anyone, make sure it isn't closed.
  2041.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  2042.   let next :: <buffer-index> = stream.buffer-next;
  2043.   let buf-size :: <buffer-index> = buf.size;
  2044.   if ((direction == #"output") | (stream.file-buffer-last-use == #"output"))
  2045.     if (next = buf-size)
  2046.       let fd = stream.file-descriptor;
  2047.       // Keep writing until fd-write manages to write everything.
  2048.       for (x :: <buffer-index>
  2049.          = call-fd-function(fd-write, fd, buf, 0, next)
  2050.          then (x + call-fd-function(fd-write, fd, buf, x, next - x)),
  2051.        until (x = next))
  2052.       end;
  2053.       values(buf, 0, buf-size)
  2054.     else
  2055.       values(buf, next, buf-size);
  2056.     end;
  2057.   else
  2058.     // The stream is both #"input-output" and was last used for #"input".
  2059.     let stop :: <buffer-index> = stream.buffer-stop;
  2060.     if (stop > next)
  2061.       // Set the file position correctly relative to the actual reading done
  2062.       // on the stream so that when users force output, it goes to the right
  2063.       // location in the file.
  2064.       call-fd-function(fd-seek, stream.file-descriptor, next - stop,
  2065.                fd-seek-current);
  2066.     end;
  2067.     stream.file-buffer-last-use := #"output";
  2068.     values(buf, 0, buf-size);
  2069.   end;
  2070. end method;
  2071.  
  2072. /// This method does not call next-method because this method does most of the
  2073. /// work determining what to do, and then only sets a slot.
  2074. ///
  2075. /// This method does not have to check whether the stream or buffer is locked
  2076. /// because release-output-buffer does that.
  2077. ///
  2078. define sealed method stream-extension-release-output-buffer
  2079.     (stream :: <fd-file-stream>, next :: <buffer-index>)
  2080.     => ();
  2081.   let direction = stream.file-direction;
  2082.   if (direction == #"input")
  2083.     error("Stream is an input stream -- %=.", stream);
  2084.   end;
  2085.   if ((direction == #"output") | (stream.file-buffer-last-use == #"output"))
  2086.     stream.buffer-next := next;
  2087.   else
  2088.     error("Buffer is currently held for input -- %=.", stream);
  2089.   end;
  2090. end method;
  2091.  
  2092. /// This method does not call next-method because it would waste time doing
  2093. /// some tests again and then only execute a few statements.
  2094. ///
  2095. define sealed method stream-extension-empty-output-buffer
  2096.     (stream :: <fd-file-stream>, stop :: <buffer-index>)
  2097.     => ();
  2098.   if (stream.file-direction == #"input")
  2099.     error("Stream is an input stream -- %=.", stream);
  2100.   end;
  2101.   if ((stream.file-direction == #"input-output") &
  2102.       (stream.file-buffer-last-use == #"input"))
  2103.     error("Buffer last used for input -- %=.", stream);
  2104.   end;
  2105.   let fd = stream.file-descriptor;
  2106.   let buf = stream.buffer;
  2107.   // Keep writing until fd-write manages to write everything.
  2108.   for (x :: <buffer-index> = call-fd-function(fd-write, fd, buf, 0, stop)
  2109.          then (x + call-fd-function(fd-write, fd, buf, x, stop - x)),
  2110.        until (x = stop))
  2111.   end;
  2112. end;
  2113.  
  2114.  
  2115.  
  2116. //// String Input Streams -- Stream Extension Protocol.
  2117. ////
  2118.  
  2119. /// The <string-input-stream> class is the class from which all other
  2120. /// string-input streams inherit.  This class cannot define slots for
  2121. /// subclasses to inherit because the stream interface makes no provision
  2122. /// for implementors of new string-input streams to access whatever commonly
  2123. /// defined slots subclasses might have.
  2124. ///
  2125. define abstract class <string-input-stream> (<random-access-stream>)
  2126. end class;
  2127.  
  2128. define method make (result-class :: singleton(<string-input-stream>),
  2129.             #rest keys, #all-keys);
  2130.   error("<string-input-stream> is not instantiable.  In this implementation "
  2131.     "of streams, you should call make on <byte-string-input-stream>.");
  2132. end method;
  2133.  
  2134. define class <byte-string-input-stream> (<string-input-stream>)
  2135.   slot buffer :: false-or(<buffer>);
  2136.   slot buffer-next :: <buffer-index>;
  2137.   slot buffer-stop :: <buffer-index>;
  2138. end class;
  2139.  
  2140. define sealed method initialize
  2141.     (stream :: <byte-string-input-stream>,
  2142.      #next next-method,
  2143.      #key string :: <byte-string> = "",
  2144.           start :: <fixed-integer> = 0,
  2145.           end: stop :: <fixed-integer> = string.size,
  2146.       size: length :: <buffer-index> = 0)
  2147.     => result :: <byte-string-input-stream>;
  2148.   ignore(length);
  2149.   // Do some bounds checking ...
  2150.   if (start < 0)
  2151.     error("Bounds error in string -- %d.", start);
  2152.   end;
  2153.   if (stop > string.size)
  2154.     error("Bounds error in string -- %d.", stop);
  2155.   end;
  2156.   if (start > stop)
  2157.     error("Start, %d, must be less than or equal to end, %d.", start, stop);
  2158.   end;
  2159.   next-method();
  2160.   // Fill in the stream's slots and copy the string into the buffer.
  2161.   let length :: <buffer-index> = stop - start;
  2162.   let buf :: <buffer> = make(<buffer>, size: length);
  2163.   stream.buffer := buf;
  2164.   copy-bytes(buf, 0, string, start, length);
  2165.   stream.buffer-next := 0;
  2166.   stream.buffer-stop := length;
  2167.   stream;
  2168. end method;
  2169.  
  2170. define sealed method close (stream :: <byte-string-input-stream>) => ();
  2171.   // Get buffer to make sure no one else holds it.
  2172.   get-input-buffer(stream);
  2173.   stream.buffer := #f;
  2174.   release-input-buffer(stream, 0, 0);
  2175. end method;
  2176.  
  2177. define sealed method stream-extension-get-input-buffer
  2178.     (stream :: <byte-string-input-stream>)
  2179.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  2180.   let buf = stream.buffer;
  2181.   // Since buffer is currently unheld by anyone, make sure it isn't closed.
  2182.   unless (buf) error("Stream has been closed -- %=.", stream); end;
  2183.   values(buf, stream.buffer-next, stream.buffer-stop);
  2184. end method;
  2185.  
  2186. define sealed method stream-extension-release-input-buffer
  2187.     (stream :: <byte-string-input-stream>, next :: <buffer-index>,
  2188.      stop :: <buffer-index>)
  2189.     => ();
  2190.   if (stop < next)
  2191.     error("Returned buffer with stop, %d, less than next, %d.", stop, next);
  2192.   else
  2193.     stream.buffer-next := next;
  2194.     stream.buffer-stop := stop;
  2195.   end;
  2196. end method;
  2197.  
  2198. define sealed method stream-extension-fill-input-buffer
  2199.     (stream :: <byte-string-input-stream>, start :: <buffer-index>)
  2200.     => stop :: <buffer-index>;
  2201.   // You can never get more input for the buffer, so return zero.
  2202.   0;
  2203. end method;
  2204.  
  2205. define sealed method stream-extension-input-available-at-source?
  2206.     (stream :: <byte-string-input-stream>)
  2207.     => available? :: <boolean>;
  2208.   // You can never get more input for the buffer.
  2209.   #f;
  2210. end method;
  2211.  
  2212.  
  2213.  
  2214. //// String Input Streams -- Random Access Protocol.
  2215. ////
  2216.  
  2217. /// All of these methods are for exported functions.
  2218. ///
  2219.  
  2220. define sealed method stream-position (stream :: <byte-string-input-stream>)
  2221.     => position :: <integer>;
  2222.   // Get the buffer to ensure no one else is using it.
  2223.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2224.     = get-input-buffer(stream);
  2225.   ignore(buf);
  2226.   release-input-buffer(stream, next, stop);
  2227.   next;
  2228. end method;
  2229.  
  2230. define sealed method stream-position-setter
  2231.     (position :: <integer>, stream :: <byte-string-input-stream>)
  2232.     => position :: <integer>;
  2233.   // Get the buffer to ensure no one else is using it.
  2234.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2235.     = get-input-buffer(stream);
  2236.   ignore(buf, next);
  2237.   if ((position < 0) | (position > stop))
  2238.     error("Illegal stream position -- %d.", position);
  2239.   end;
  2240.   release-input-buffer(stream, position, stop);
  2241.   position;
  2242. end method;
  2243.  
  2244. /// This method does not call stream-position-setter because this method
  2245. /// does most of the work determining what to do, and then just releases
  2246. /// the buffer.
  2247. ///
  2248. define sealed method adjust-stream-position
  2249.     (offset :: <integer>,
  2250.      stream :: <byte-string-input-stream>,
  2251.      #key from: reference :: one-of(#"start", #"current", #"end") = #"start")
  2252.     => position :: <integer>;
  2253.   // Get the buffer to ensure no one else is using it.
  2254.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2255.     = get-input-buffer(stream);
  2256.   ignore(buf);
  2257.   let position = select (reference)
  2258.            (#"start") => offset;
  2259.            (#"current") => (next + offset);
  2260.            (#"end") => (stop + offset);
  2261.          end;
  2262.   if ((position < 0) | (position > stop))
  2263.     error("Illegal stream position -- %d.", position);
  2264.   end;
  2265.   release-input-buffer(stream, position, stop);
  2266.   position;
  2267. end method;
  2268.  
  2269. define sealed method stream-size (stream :: <byte-string-input-stream>)
  2270.     => size :: <integer>;
  2271.   // Get the buffer to ensure no one else is using it.
  2272.   let (buf, next :: <buffer-index>, stop :: <buffer-index>)
  2273.     = get-input-buffer(stream);
  2274.   ignore(buf);
  2275.   release-input-buffer(stream, next, stop);
  2276.   stop;
  2277. end method;
  2278.  
  2279.  
  2280.  
  2281. //// String Output Streams -- classes, protocol, and Stream Extension Protocol.
  2282. ////
  2283.  
  2284. /// The <string-output-stream> class is the class from which all other
  2285. /// string-output streams inherit.  This class cannot define slots for
  2286. /// subclasses to inherit because the stream interface makes no provision
  2287. /// for implementors of new string-output streams to access whatever commonly
  2288. /// defined slots subclasses might have.
  2289. ///
  2290. define abstract class <string-output-stream> (<random-access-stream>)
  2291. end class;
  2292.  
  2293. define method make (result-class :: singleton(<string-output-stream>),
  2294.             #rest keys, #all-keys);
  2295.   error("<string-output-stream> is not instantiable.  In this implementation "
  2296.     "of streams, you should call make on <byte-string-output-stream>.");
  2297. end method;
  2298.  
  2299. /// This class collects its output in a buffer.  This makes mutual exclusion
  2300. /// easier because internal code can use the Buffer Access Protocol.  Also,
  2301. /// because the sequence operations in Dylan are nearly worthless, internal
  2302. /// code can use the <buffer> protocol to copy stuff around.  This saves
  2303. /// writing our own string to string copying routines.
  2304. ///
  2305. define class <byte-string-output-stream> (<string-output-stream>)
  2306.   slot buffer :: false-or(<buffer>);
  2307.   slot string-output-stream-backup :: false-or(<byte-string>),
  2308.     init-value: #f;
  2309.   //
  2310.   // This slot holds the current position for writing into the buffer.
  2311.   slot buffer-next :: <buffer-index>, init-value: 0;
  2312.   //
  2313.   // This slot holds the end of the output held in the buffer.  Because of the
  2314.   // Random Access Protocol buffer-next may not be at the end of all the output
  2315.   // written.
  2316.   slot buffer-stop :: <buffer-index>, init-value: 0;
  2317. end class;
  2318.  
  2319. /// This method does not call register-output-stream because it is
  2320. /// meaningless to force output on a <byte-string-output-stream> when the
  2321. /// application exits.
  2322. ///
  2323. define sealed method initialize
  2324.     (stream :: <byte-string-output-stream>,
  2325.      #next next-method,
  2326.      #key size: length :: <buffer-index> = $default-buffer-size)
  2327.     => result :: <byte-string-output-stream>;
  2328.   stream.buffer := make(<buffer>, size: length);
  2329.   stream;
  2330. end method;
  2331.  
  2332. /// string-output-stream-string -- Exported.
  2333. ///
  2334. define generic string-output-stream-string (stream :: <string-output-stream>)
  2335.     => output :: <string>;
  2336.  
  2337. /// string-output-stream-string -- Method for Exported Interface.
  2338. ///
  2339. /// Collect the output backed up in the stream as a <byte-string> and
  2340. /// the pending output in the stream's buffer, and return this as a
  2341. /// <byte-string>.
  2342. ///                        
  2343. define sealed method string-output-stream-string
  2344.     (stream :: <byte-string-output-stream>)
  2345.     => output :: <byte-string>;
  2346.   let buf :: <buffer> = get-output-buffer(stream);
  2347.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2348.   let output-len :: <fixed-integer> = stream.buffer-stop;
  2349.   let string
  2350.     = case
  2351.     (~ backup) =>
  2352.       // The only output is what is in the buffer.
  2353.       let res = make(<byte-string>, size: output-len);
  2354.       copy-bytes(res, 0, buf, 0, output-len);
  2355.       res;
  2356.     (output-len = 0) =>
  2357.       // The only output is what is in the backup string.
  2358.       backup;
  2359.     otherwise =>
  2360.       // Get output from both the backup string and the buffer.
  2361.       let backup-len :: <fixed-integer> = backup.size;
  2362.       let res :: <byte-string>
  2363.         = make(<byte-string>, size: (backup-len + output-len));
  2364.       copy-bytes(res, 0, backup, 0, backup-len);
  2365.       copy-bytes(res, backup-len, buf, 0, output-len);
  2366.       res;
  2367.       end;
  2368.   stream.string-output-stream-backup := #f;
  2369.   stream.buffer-stop := 0;
  2370.   release-output-buffer(stream, 0);
  2371.   string;
  2372. end method;
  2373.  
  2374. /// close -- Method for Exported Interface.
  2375. ///
  2376. define sealed method close (stream :: <byte-string-output-stream>) => ();
  2377.   // Get the buffer to make sure no one is using it.
  2378.   get-output-buffer(stream);
  2379.   stream.buffer := #f;
  2380.   unregister-output-stream(stream);
  2381.   release-output-buffer(stream, 0);
  2382. end method;
  2383.  
  2384. /// stream-extension-get-output-buffer -- Method for Exported Interface.
  2385. ///
  2386. /// This must not return a full buffer.  When the buffer is full, this
  2387. /// creates a backup store using a <byte-string>.  If there is already a
  2388. /// backup string, then this function creates a new one to hold all the
  2389. /// previously backed up output and what is in the buffer.
  2390. ///
  2391. define sealed method stream-extension-get-output-buffer
  2392.     (stream :: <byte-string-output-stream>)
  2393.     => (buffer :: <buffer>, next :: <buffer-index>, stop :: <buffer-index>);
  2394.   let buf :: <buffer> = stream.buffer;
  2395.   let buf-next :: <buffer-index> = stream.buffer-next;
  2396.   let buf-len :: <buffer-index> = buf.size;
  2397.   // Test buf-next rather that buffer-stop.  Though buffer-stop may indicate
  2398.   // the buffer is full, there's no reason to back up the buffer when the
  2399.   // buf-next says the user isn't writing off the end of the buffer.
  2400.   if (buf-next = buf-len)
  2401.     // Can't write further in the buffer.
  2402.     let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2403.     if (backup)
  2404.       // Concatenate the backup and buffer to form new backup string.
  2405.       let backup-len :: <fixed-integer> = backup.size;
  2406.       let new-backup-len = backup-len + buf-len;
  2407.       let res :: <byte-string>
  2408.     = make(<byte-string>, size: new-backup-len);
  2409.       copy-bytes(res, 0, backup, 0, backup-len);
  2410.       copy-bytes(res, backup-len, buf, 0, buf-len);
  2411.       stream.string-output-stream-backup := res;
  2412.     else
  2413.       // Just copy the buffer into a backup string.
  2414.       stream.string-output-stream-backup :=
  2415.         buffer-subsequence(buf, <byte-string>, 0, buf-len);
  2416.     end;
  2417.     // Make sure buffer-stop is maintained correctly, and we move any output
  2418.     // remaining in the buffer to the beginning of the buffer.  This ensure
  2419.     // the output is correctly placed to be overwritten.  We do not update
  2420.     // buffer-next since we must rely on the user's value when he releases
  2421.     // the buffer.
  2422.     let stop :: <buffer-index> = stream.buffer-stop;
  2423.     if (stop > buf-next)
  2424.       let new-stop :: <buffer-index> = (stop - buf-next);
  2425.       copy-bytes(buf, 0, buf, buf-next, new-stop);
  2426.       stream.buffer-stop := new-stop;
  2427.     else
  2428.       stream.buffer-stop := 0;
  2429.     end;
  2430.     values(buf, 0, buf-len);
  2431.   else
  2432.     // Just return the values, nothing special to do.
  2433.     values(buf, buf-next, buf-len);
  2434.   end;
  2435. end method;
  2436.  
  2437. define sealed method stream-extension-release-output-buffer
  2438.     (stream :: <byte-string-output-stream>, next :: <buffer-index>)
  2439.     => ();
  2440.   stream.buffer-next := next;
  2441.   if (next > stream.buffer-stop) stream.buffer-stop := next end;
  2442. end method;
  2443.  
  2444. define sealed method stream-extension-empty-output-buffer
  2445.     (stream :: <byte-string-output-stream>, stop :: <buffer-index>)
  2446.     => ();
  2447.   let buf :: <buffer> = stream.buffer;
  2448.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2449.   if (backup)
  2450.     // Add output in buffer to backup.
  2451.     let backup-len :: <fixed-integer> = backup.size;
  2452.     let new-backup-len = backup-len + stop;
  2453.     let res :: <byte-string>
  2454.       = make(<byte-string>, size: new-backup-len);
  2455.     copy-bytes(res, 0, backup, 0, backup-len);
  2456.     copy-bytes(res, backup-len, buf, 0, stop);
  2457.     stream.string-output-stream-backup := res;
  2458.   else
  2459.     // Just create a backup string.
  2460.     stream.string-output-stream-backup
  2461.       := buffer-subsequence(buf, <byte-string>, 0, stop);
  2462.   end;
  2463.   // Make sure buffer-stop is maintained correctly, and we move any left over
  2464.   // output to the beginning of the buffer to be overwritten.  We do not
  2465.   // update buffer-next since we must rely on the user's value when he releases
  2466.   // the buffer.
  2467.   let real-stop :: <buffer-index> = stream.buffer-stop;
  2468.   if (real-stop > stop)
  2469.     let new-stop :: <buffer-index> = (real-stop - stop);
  2470.     copy-bytes(buf, 0, buf, stop, new-stop);
  2471.     stream.buffer-stop := new-stop;
  2472.   else
  2473.     stream.buffer-stop := 0;
  2474.   end;
  2475. end method;
  2476.  
  2477. define sealed method stream-extension-synchronize
  2478.     (stream :: <byte-string-output-stream>)
  2479.     => ();
  2480. end method;
  2481.  
  2482.  
  2483.  
  2484. //// String output streams -- Random Access Protocol.
  2485. ////
  2486.  
  2487. /// All of these methods are for exported functions.
  2488. ///
  2489.  
  2490. define method stream-position (stream :: <byte-string-output-stream>)
  2491.     => position :: <integer>;
  2492.   // Get the output buffer to make sure the stream is not already in use.
  2493.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  2494.   ignore(buf);
  2495.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2496.   release-output-buffer(stream, next);
  2497.   if (backup)
  2498.     backup.size + next;
  2499.   else
  2500.     next;
  2501.   end;
  2502. end method;
  2503.  
  2504. define method stream-position-setter (position :: <integer>,
  2505.                       stream :: <byte-string-output-stream>)
  2506.     => position :: <integer>;
  2507.   // Get the output buffer to make sure the stream is not already in use.
  2508.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  2509.   let stop :: <buffer-index> = stream.buffer-stop;
  2510.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2511.   let backup-len :: <integer> = if (backup) backup.size else 0 end;
  2512.   let stream-len :: <integer> = backup-len + stop;
  2513.   if ((position < 0) | (position > stream-len))
  2514.     error("Illegal stream position -- %d.", position);
  2515.   end;
  2516.   if (position >= backup-len)
  2517.     // Reposition within the existing buffer.
  2518.     release-output-buffer(stream, (position - backup-len));
  2519.   else
  2520.     new-string-output-stream-backup(stream, buf, stop, backup, backup-len);
  2521.     release-output-buffer(stream, position);
  2522.   end;
  2523.   position;
  2524. end method;
  2525.  
  2526. /// This could be a literal constant in the following method definition, but
  2527. /// Dylan failed to incorporate any means for cleanly identifying non-printing
  2528. /// characters in character and string literals.  I don't want to use my
  2529. /// editor to quote non-printing characters into my program's source.
  2530. ///
  2531. define constant $null-char = as(<byte-character>, 0);
  2532.  
  2533. define method adjust-stream-position
  2534.     (offset :: <integer>,
  2535.      stream :: <byte-string-output-stream>,
  2536.      #key from: reference :: one-of(#"start", #"current", #"end") = #"start")
  2537.     => position :: <integer>;
  2538.   // Get the output buffer to make sure the stream is not already in use.
  2539.   let (buf :: <buffer>, buf-next :: <buffer-index>)
  2540.     = get-output-buffer(stream);
  2541.   let stop :: <buffer-index> = stream.buffer-stop;
  2542.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2543.   let backup-len :: <integer> = if (backup) backup.size else 0 end;
  2544.   let stream-len :: <integer> = backup-len + stop;
  2545.   let position = select (reference)
  2546.            (#"start") => offset;
  2547.            (#"current") => (buf-next + offset);
  2548.            (#"end") => (stream-len + offset);
  2549.          end;
  2550.   case
  2551.     (position < 0) =>
  2552.       error("Illegal stream position -- %d.", position);
  2553.     ((position >= backup-len) & (position <= stream-len)) =>
  2554.       release-output-buffer(stream, (position - backup-len));
  2555.     (position > stream-len) =>
  2556.       // Get output from both the backup string and the buffer.
  2557.       let new-backup = make(<byte-string>, size: position);
  2558.       if (backup)
  2559.     copy-bytes(new-backup, 0, backup, 0, backup-len);
  2560.       end;
  2561.       copy-bytes(new-backup, backup-len, buf, 0, stop);
  2562.       for (i from (backup-len + stop) below position)
  2563.     new-backup[i] := $null-char;
  2564.       end;
  2565.       stream.string-output-stream-backup := new-backup;
  2566.       stream.buffer-stop := 0;
  2567.       release-output-buffer(stream, 0);
  2568.     otherwise =>
  2569.       new-string-output-stream-backup(stream, buf, stop, backup, backup-len);
  2570.       release-output-buffer(stream, position);
  2571.   end;
  2572.   position;
  2573. end method;
  2574.  
  2575. /// new-string-output-stream-backup -- Internal
  2576. ///
  2577. /// This function implements file-position-setter and adjust-file-position
  2578. /// when the new position is in the backup string.  This function just moves
  2579. /// everything into a new buffer and loses the backup.
  2580. ///
  2581. /// This method assumes buffers can hold as much as backup strings; however,
  2582. /// the rest of this streams implementation uses <integer> indexes for strings
  2583. /// and <fixed-integer> indexes for buffers.  It could be that a backup string
  2584. /// could grow to a size that no buffer could hold it, but that's pretty
  2585. /// unlikely in most implementations.  If it should ever happen, the make call
  2586. /// to get a new buffer should flame out, and someone will have to write a
  2587. /// better implementation of <byte-string-output-stream>s.
  2588. ///
  2589. define method new-string-output-stream-backup
  2590.     (stream :: <stream>, buf :: <buffer>, stop :: <buffer-index>,
  2591.      backup :: <byte-string>, backup-len :: <integer>)
  2592.   // Create a new buffer to hold the backup's, if any, and the current
  2593.   // buffer's contents.  Throw away the old buffer and backup.
  2594.   let new-buf = make(<buffer>, size: (backup-len + buf.size));
  2595.   if (backup)
  2596.     copy-bytes(new-buf, 0, backup, 0, backup-len);
  2597.   end;
  2598.   copy-bytes(new-buf, backup-len, buf, 0, stop);
  2599.   stream.buffer := new-buf;
  2600.   stream.buffer-stop := (backup-len + stop);
  2601.   stream.string-output-stream-backup := #f;
  2602. end method;
  2603.  
  2604. define method stream-size (stream :: <byte-string-output-stream>)
  2605.     => size :: <integer>;
  2606.   // Get the output buffer to make sure the stream is not already in use.
  2607.   let (buf :: <buffer>, next :: <buffer-index>) = get-output-buffer(stream);
  2608.   ignore(buf);
  2609.   let backup :: false-or(<byte-string>) = stream.string-output-stream-backup;
  2610.   release-output-buffer(stream, next);
  2611.   if (backup)
  2612.     backup.size + stream.buffer-stop;
  2613.   else
  2614.     stream.buffer-stop;
  2615.   end;
  2616. end method;
  2617.  
  2618.  
  2619.  
  2620. //// Buffer Protocol.
  2621. ////
  2622.  
  2623. /// The <buffer> class as <vector> is implemented in the System module of
  2624. /// the Dylan library.
  2625. ///
  2626.  
  2627. define generic buffer-subsequence
  2628.     (buf :: <buffer>, result-class :: <class>,
  2629.      start :: <buffer-index>, stop :: <buffer-index>)
  2630.     => result :: <sequence>;
  2631.  
  2632. define sealed method buffer-subsequence
  2633.     (buf :: <buffer>,
  2634.      result-class :: one-of(<byte-string>, <byte-vector>, <buffer>),
  2635.      start :: <buffer-index>, stop :: <buffer-index>)
  2636.     => result :: type-or(<byte-string>, <byte-vector>, <buffer>);
  2637.   if (stop > buf.size)
  2638.     error("Bounds error in buffer -- %d.", stop);
  2639.   end;
  2640.   if (start < 0)
  2641.     error("Bounds error in buffer -- %d.", start);
  2642.   end;
  2643.   let len = (stop - start);
  2644.   let res :: <byte-string> = make(result-class, size: len);
  2645.   copy-bytes(res, 0, buf, start, len);
  2646. end method;
  2647.  
  2648.  
  2649. /// copy-from-buffer! -- Exported.
  2650. ///
  2651. define generic copy-from-buffer!
  2652.     (destination :: <sequence>, buf :: <buffer>, buf-start :: <buffer-index>,
  2653.      #key start: :: <fixed-integer>, // = 0
  2654.           end: :: <fixed-integer>) // = destination.size)
  2655.     => ();
  2656.  
  2657. define sealed method copy-from-buffer!
  2658.     (destination :: type-or(<byte-string>, <byte-vector>, <buffer>),
  2659.      buf :: <buffer>,
  2660.      buf-start :: <buffer-index>,
  2661.      #key start :: <fixed-integer> = 0,
  2662.           end: stop :: <fixed-integer> = destination.size)
  2663.     => ();
  2664.   // Do lots of bounds checking.
  2665.   if ((buf-start + (stop - start))  > buf.size)
  2666.     error("Insufficient number of bytes in buffer after specified start, %d.",
  2667.       buf-start);
  2668.   end;
  2669.   if (buf-start < 0)
  2670.     error("Bounds error in buffer -- %d.", buf-start);
  2671.   end;
  2672.   if (start < 0)
  2673.     error("Bounds error in destination -- %d.", start);
  2674.   end;
  2675.   if (stop > destination.size)
  2676.     error("Bounds error in destination -- %d.", stop);
  2677.   end;
  2678.   if (start > stop)
  2679.     error("Start, %d, must be less than or equal to end, %d.", start, stop);
  2680.   end;
  2681.   // Do the copy.
  2682.   copy-bytes(destination, start, buf, buf-start, (stop - start));
  2683. end method;
  2684.  
  2685.  
  2686.  
  2687. /// copy-into-buffer! -- Exported.
  2688. ///
  2689. define generic copy-into-buffer!
  2690.     (source :: <sequence>, buf :: <buffer>, buf-start :: <buffer-index>,
  2691.      #key start: :: <fixed-integer>, // = 0,
  2692.           end: :: <fixed-integer>) // = source.size)
  2693.     => ();
  2694.  
  2695. define sealed method copy-into-buffer!
  2696.     (source :: type-or(<byte-string>, <byte-vector>, <buffer>),
  2697.      buf :: <buffer>, buf-start :: <buffer-index>,
  2698.      #key start :: <fixed-integer> = 0,
  2699.           end: stop :: <fixed-integer> = source.size)
  2700.     => ();
  2701.   // Do lots of bounds checking.
  2702.   if (start < 0)
  2703.     error("Bounds error in source -- %d.", start);
  2704.   end;
  2705.   if (stop > source.size)
  2706.     error("Bounds error in source -- %d.", stop);
  2707.   end;
  2708.   if (start > stop)
  2709.     error("Start, %d, must be less than or equal to end, %d.", start, stop);
  2710.   end;
  2711.   if (buf-start < 0)
  2712.     error("Bounds error in buffer -- %d.", buf-start);
  2713.   end;
  2714.   if ((buf-start + (stop - start))  > buf.size)
  2715.     error("Insufficient number of bytes in buffer after specified start, %d.",
  2716.       buf-start);
  2717.   end;
  2718.   // Do the copy.
  2719.   copy-bytes(buf, buf-start, source, start, (stop - start));
  2720. end method;
  2721.